Switch to Koa, more es6

This commit is contained in:
Jonathan Cremin 2015-06-03 21:45:54 -07:00
parent 1090affc9c
commit b3abff99ae
36 changed files with 25573 additions and 928 deletions

View file

@ -1,4 +1,10 @@
{ {
"ecmaFeatures": {
"modules": true
},
"rules": {
"quotes": [2, "single", "avoid-escape"]
},
"env": { "env": {
"node": true, "node": true,
"es6": true "es6": true

189
app.js
View file

@ -1,127 +1,102 @@
"use strict"; import path from 'path';
var express = require("express"); import koa from 'koa';
var helmet = require("helmet"); import route from 'koa-route';
var path = require("path"); import logger from 'koa-logger';
var favicon = require("serve-favicon"); import favicon from 'koa-favicon';
var logger = require("morgan"); import compress from 'koa-compress';
var session = require("express-session"); import staticHandler from 'koa-static';
var cookieParser = require("cookie-parser"); import bodyparser from 'koa-bodyparser';
var flash = require("connect-flash"); import React from 'react';
var compress = require("compression"); import co from 'co';
var bodyParser = require("body-parser"); import db from './config/db';
var pmongo = require("promised-mongo"); import index from './routes/index';
import search from './routes/search';
import share from './routes/share';
import itunesProxy from './routes/itunes-proxy';
import {routes} from './views/app.jsx';
import zlib from 'zlib';
import createHandler from './lib/react-handler';
var index = require("./routes/index"); import debuglog from 'debug';
var search = require("./routes/search"); const debug = debuglog('match.audio');
var share = require("./routes/share");
var itunesProxy = require("./routes/itunes-proxy");
var React = require("react"); const app = koa();
require("node-jsx").install({extension: ".jsx"});
var ErrorView = React.createFactory(require("./views/error.jsx")); app.use(function* (next) {
this.set('Server', 'Nintendo 64');
try {
yield next;
} catch (err) {
if (!err.status) {
console.error(err.stack);
} else if (err.status === 404) {
let Handler = yield createHandler(routes, this.request.url);
var browserify = require("connect-browserify"); let App = React.createFactory(Handler);
let content = React.renderToString(new App());
var app = express(); this.body = '<!doctype html>\n' + content;
} else {
var development = process.env.NODE_ENV !== "production"; throw err;
}
// view engine setup }
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");
app.use(compress());
app.use(favicon(path.join(__dirname, "/public/images/favicon.png")));
app.use(helmet());
app.use(logger("dev"));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(session({
secret: "keyboard catz",
resave: false,
saveUninitialized: true
}));
app.use(flash());
app.use(express.static(path.join(__dirname, "public")));
var dbUrl = process.env.MONGOHQ_URL || "mongodb://localhost/match-audio";
app.use(function(req, res, next) {
req.db = res.db = pmongo(dbUrl, ["matches"]);
next();
}); });
if (development) { app.use(bodyparser());
app.get("/javascript/bundle.js", app.use(compress({flush: zlib.Z_SYNC_FLUSH }));
browserify("./views/app.jsx", { app.use(favicon(path.join(__dirname, '/public/images/favicon.png')));
debug: true, app.use(logger());
watch: true app.use(staticHandler(path.join(__dirname, 'public')));
}));
}
app.get("*", function(req, res, next) { let mongo = {};
co(function*() {
mongo = yield db();
});
app.use(function* (next){
this.db = mongo;
yield next;
});
app.use(function* (next) {
// force SSL // force SSL
if (req.headers["cf-visitor"] && req.headers["cf-visitor"] !== "{\"scheme\":\"https\"}") { if (this.headers['cf-visitor'] && this.headers['cf-visitor'] !== '{"scheme":"https"}') {
return res.redirect("https://" + req.headers.host + req.url); return this.redirect('https://' + this.headers.host + this.url);
} else if (req.headers["cf-visitor"]) { } else if (this.headers['cf-visitor']) {
req.userProtocol = "https"; this.userProtocol = 'https';
} else { } else {
req.userProtocol = "http"; this.userProtocol = 'http';
} }
// redirect www // redirect www
if (req.headers.host.match(/^www/) !== null ) { if (this.headers.host.match(/^www/) !== null ) {
return res.redirect(req.userProtocol + "://" + req.headers.host.replace(/^www\./, "") + req.url); return this.redirect(this.userProtocol + '://' + this.headers.host.replace(/^www\./, '') + this.url);
} else { } else {
next(); yield next;
} }
}); });
app.get("/", index); app.use(route.get('/', index));
app.post("/search", search); app.use(route.post('/search', search));
app.get("/:service/:type/:id.:format?", share); app.use(route.get('/itunes/(.*)', itunesProxy));
app.get("/itunes/*", itunesProxy); app.use(route.get('/:service/:type/:id.:format?', share));
app.get("/recent", function(req, res, next) { app.use(route.get('/recent', function* () {
req.db.matches.find().sort({"created_at": -1}).limit(6).toArray().then(function(docs){ let recents = [];
var recents = []; let docs = yield this.db.matches.find().sort({'created_at': -1}).limit(6).toArray();
docs.forEach(function(doc) { docs.forEach(function(doc) {
recents.push(doc.services[doc._id.split("$$")[0]]); // eslint-disable-line no-underscore-dangle recents.push(doc.services[doc._id.split('$$')[0]]); // eslint-disable-line no-underscore-dangle
});
res.json({recents: recents});
}).catch(function (error) {
return next(error);
}); });
}); this.body = {recents: recents};
}));
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error("Not Found");
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get("env") === "development") {
app.use(function(err, req, res) {
console.log(err.stack);
res.status(err.status || 500);
var content = React.renderToString(new ErrorView({status: err.status || 500, message: err.message, error: err}));
res.send("<!doctype html>\n" + content);
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res) {
res.status(err.status || 500);
var content = React.renderToString(new ErrorView({status: err.status || 500, message: err.message, error: {status: err.status || 500}}));
res.send("<!doctype html>\n" + content);
});
app.use(route.get('*', function* () {
this.throw(404);
}));
module.exports = app; module.exports = app;
if (!module.parent) {
app.listen(process.env.PORT || 3000, function() {
debug('Koa server listening on port ' + (process.env.PORT || 3000));
});
}

10
bin/www
View file

@ -1,10 +1,10 @@
#!/usr/bin/env node #!/usr/bin/env node
"use strict"; "use strict";
var debug = require('debug')('match.audio'); var debug = require("debug")("match.audio");
var app = require('../app'); var app = require("../app");
app.set('port', process.env.PORT || 3000); app.set("port", process.env.PORT || 3000);
var server = app.listen(app.get('port'), function() { var server = app.listen(app.get("port"), function() {
debug('Express server listening on port ' + server.address().port); debug("Express server listening on port " + server.address().port);
}); });

17
config/db.js Normal file
View file

@ -0,0 +1,17 @@
'use strict';
const debug = require('debug')('match.audio');
// Shut mongodb-promisified up.
const dir = console.dir;
console.dir = function() {};
const MongoClient = require('mongodb-promisified')().MongoClient;
console.dir = dir;
const uristring = process.env.MONGOLAB_URI || process.env.MONGOHQ_URL || 'mongodb://localhost:27017/match-audio';
module.exports = function*() {
const client = yield MongoClient.connect(uristring);
debug('Successfully connected to Mongodb');
client.matches = client.collection('matches');
return client;
};

View file

@ -1,10 +1,10 @@
"use strict"; import path from 'path';
var path = require("path"); import fs from 'fs';
var services = []; var services = [];
require("fs").readdirSync(path.join(__dirname, "services")).forEach(function(file) { fs.readdirSync(path.join(__dirname, 'services')).forEach(function(file) {
var service = require(path.join(__dirname, "services", file)); var service = require(path.join(__dirname, 'services', file));
if (service.search) { if (service.search) {
services.push(service); services.push(service);
} }

19
lib/react-handler.js vendored Normal file
View file

@ -0,0 +1,19 @@
var Router = require('react-router');
export default function* (routes, url) {
let router = Router.create({
location: url,
routes: routes,
onAbort(aborted) {
let { to, params, query } = aborted;
this.redirect(Router.makePath(to, params, query));
}
});
return new Promise(function(resolve) {
router.run((Handler) => {
resolve(Handler);
});
});
}

View file

@ -1,12 +1,11 @@
"use strict"; import path from 'path';
var path = require("path"); import fs from 'fs';
module.exports = []; module.exports = [];
require("fs").readdirSync(path.join(__dirname, "services")).forEach(function(file) { fs.readdirSync(path.join(__dirname, 'services')).forEach(function(file) {
var service = require(path.join(__dirname, "services", file)); var service = require(path.join(__dirname, 'services', file));
if (service.search) { if (service.search) {
module.exports.push(service); module.exports.push(service);
} }
}); });

View file

@ -1,150 +0,0 @@
"use strict";
var parse = require("url").parse;
var request = require("superagent");
require("superagent-bluebird-promise");
module.exports.id = "beats";
if (!process.env.BEATS_KEY || !process.env.BEATS_SECRET) {
console.warn("BEATS_KEY or BEATS_SECRET environment variables not found, deactivating Beats.");
return;
}
var credentials = {
key: process.env.BEATS_KEY,
secret: process.env.BEATS_SECRET
};
var apiRoot = "https://partner.api.beatsmusic.com/v1/api";
module.exports.match = require("./url").match;
module.exports.parseUrl = function(url) {
var matches = parse(url).path.match(/\/albums[\/]+([^\/]+)(\/tracks\/)?([^\/]+)?/);
if (matches && matches[3]) {
return module.exports.lookupId(matches[3], "track");
} else if (matches && matches[1]) {
return module.exports.lookupId(matches[1], "album");
} else {
throw new Error("Url does not match");
}
};
module.exports.lookupId = function(id, type) {
if (type === "album") {
return request.get(apiRoot + "/albums/" + id + "/images/default?size=large&client_id=" + credentials.key).redirects(0).promise().then(function(res) {
var artwork = {large: res.headers.location.replace("http:", "https:")};
return request.get(apiRoot + "/albums/" + id + "/images/default?client_id=" + credentials.key).redirects(0).promise().then(function(res) {
artwork.small = res.headers.location.replace("http:", "https:");
return request.get(apiRoot + "/albums/" + id + "?client_id=" + credentials.key).promise().then(function(res) {
if (!res.body.data) {
var error = new Error("Not Found");
error.status = 404;
throw error;
}
var result = res.body.data;
return {
service: "beats",
type: "album",
id: result.id,
name: result.title,
streamUrl: "https://listen.beatsmusic.com/albums/" + result.id,
purchaseUrl: null,
artwork: artwork,
artist: {
name: result.artist_display_name
}
};
});
});
});
} else if (type === "track") {
return request.get(apiRoot + "/tracks/" + id + "?client_id=" + credentials.key).promise().then(function(res) {
if (!res.body.data) {
var error = new Error("Not Found");
error.status = 404;
throw error;
}
var result = res.body.data;
return request.get(apiRoot + "/albums/" + result.refs.album.id + "/images/default?size=large&client_id=" + credentials.key).redirects(0).promise().then(function(res) {
var artwork = {large: res.headers.location.replace("http:", "https:")};
return request.get(apiRoot + "/albums/" + result.refs.album.id + "/images/default?client_id=" + credentials.key).redirects(0).promise().then(function(res) {
artwork.small = res.headers.location.replace("http:", "https:");
return {
service: "beats",
type: "track",
id: result.id,
name: result.title,
streamUrl: "https://listen.beatsmusic.com/albums/" + result.refs.album.id + "/tracks/" + result.id,
purchaseUrl: null,
artwork: artwork,
artist: {
name: result.artist_display_name
},
album: {
name: result.refs.album.display
}
};
});
});
});
} else {
var error = new Error("Not Found");
error.status = 404;
return error;
}
};
module.exports.search = function(data) {
var cleanParam = function(str) {
return str.replace(/[\:\?\&]+/, "");
};
var query, album;
var type = data.type;
if (type === "album") {
query = "'" + cleanParam(data.artist.name) + "' '" + cleanParam(data.name) + "'";
album = data.name;
} else if (type === "track") {
query = "'" + cleanParam(data.artist.name) + "' '" + cleanParam(data.name) + "'";
if (data.album) {
album = data.album.name;
} else {
album = "";
}
}
var path = "/search?q=" + encodeURIComponent(query) + "&type=" + type + "&client_id=" + credentials.key;
return request.get(apiRoot + path).promise().then(function(res) {
if (!res.body.data[0]) {
return {service: "beats"};
} else {
var found;
var choppedAlbum = data.type === "album" ? cleanParam(data.name) : cleanParam(data.album.name);
var choppedArtist = cleanParam(data.artist.name);
res.body.data.forEach(function(item) {
var matches = item.detail.match(/^[^\(\[]+/);
if(choppedArtist.indexOf(matches[0]) >= 0) {
found = item;
}
});
if (!found && !choppedAlbum.length) {
return module.exports.lookupId(res.body.data[0].id, type);
}
res.body.data.forEach(function(item) {
var matches = item.related.display.match(/^[^\(\[]+/);
if(choppedAlbum.indexOf(matches[0]) >= 0) {
found = item;
}
});
if (!found) {
return {service: "beats"};
}
return module.exports.lookupId(found.id, type);
}
});
};

View file

@ -1,11 +0,0 @@
"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;
};

View file

@ -1,109 +1,110 @@
"use strict"; import {parse} from 'url';
var parse = require('url').parse; import request from 'superagent';
var Promise = require('bluebird'); import 'superagent-bluebird-promise';
var request = require('superagent');
require('superagent-bluebird-promise');
module.exports.id = "deezer"; module.exports.id = 'deezer';
var apiRoot = "https://api.deezer.com"; const apiRoot = 'https://api.deezer.com';
module.exports.match = require('./url').match; module.exports.match = require('./url').match;
module.exports.parseUrl = function(url) { module.exports.parseUrl = function(url) {
var matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/); let matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/);
if (matches && matches[2]) { if (matches && matches[2]) {
return module.exports.lookupId(matches[2], matches[1]); return module.exports.lookupId(matches[2], matches[1]);
} else { } else {
throw new Error(); throw new Error();
} }
}
module.exports.lookupId = function(id, type) {
var path = "/" + type + "/" + id;
return request.get(apiRoot + path).promise().then(function(res) {
var result = res.body;
if (res.body.error) {
var error = new Error("Not Found");
error.status = 404;
throw error;
}
var cover = result.cover || result.album.cover;
return request.get(cover).redirects(0).promise().then(function(res) {
var artwork = {
small: res.headers.location.replace("120x120", "200x200"),
large: res.headers.location.replace("120x120", "800x800")
};
if (type == "album") {
return {
service: "deezer",
type: type,
id: result.id,
name: result.title,
streamUrl: result.link,
purchaseUrl: null,
artwork: artwork,
artist: {
name: result.artist.name
},
};
} else if (type == "track") {
return {
service: "deezer",
type: type,
id: result.id,
name: result.title,
streamUrl: result.album.link,
purchaseUrl: null,
artwork: artwork,
artist: {
name: result.artist.name
},
album: {
name: result.album.title
}
};
} else {
throw new Error();
}
});
});
}; };
module.exports.search = function(data) { module.exports.lookupId = function* (id, type) {
var cleanParam = function(str) { let path = '/' + type + '/' + id;
return str.replace(/[\:\?\&]+/, "");
}
var query, album;
var type = data.type;
if (type == "album") { let {body} = yield request.get(apiRoot + path).promise();
query = cleanParam(data.artist.name) + " " + cleanParam(data.name); if (!body || body.error) {
let error = new Error('Not Found');
error.status = 404;
return Promise.reject(error);
}
let item = body;
let coverUrl = item.cover || item.album.cover;
let cover = 'test';
// nasty hacks for superagent-bluebird-promise
try {
cover = yield request.get(coverUrl).redirects(0);
} catch(err) {
cover = err.message.response.res;
}
let artwork = {
small: cover.headers.location.replace('120x120', '200x200'),
large: cover.headers.location.replace('120x120', '800x800')
};
if (type === 'album') {
return Promise.resolve({
service: 'deezer',
type: type,
id: item.id,
name: item.title,
streamUrl: item.link,
purchaseUrl: null,
artwork: artwork,
artist: {
name: item.artist.name
}
});
} else if (type === 'track') {
return Promise.resolve({
service: 'deezer',
type: type,
id: item.id,
name: item.title,
streamUrl: item.album.link,
purchaseUrl: null,
artwork: artwork,
artist: {
name: item.artist.name
},
album: {
name: item.album.title
}
});
} else {
return Promise.reject(new Error());
}
};
module.exports.search = function* (data) {
let cleanParam = function(str) {
return str.replace(/[\:\?\&]+/, '');
};
let query, album;
let {type} = data;
if (type === 'album') {
query = cleanParam(data.artist.name) + ' ' + cleanParam(data.name);
album = data.name; album = data.name;
} else if (type == "track") { } else if (type === 'track') {
query = cleanParam(data.artist.name) + " " + cleanParam(data.album.name) + " " + cleanParam(data.name); query = cleanParam(data.artist.name) + ' ' + cleanParam(data.album.name) + ' ' + cleanParam(data.name);
album = data.album.name; album = data.album.name;
} }
var path = "/search/" + type + "?q=" + encodeURIComponent(query); var path = '/search/' + type + '?q=' + encodeURIComponent(query);
return request.get(apiRoot + path).promise().then(function(res) { let response = yield request.get(apiRoot + path);
if (!res.body.data[0]) { if (response.body.data[0]) {
var matches = album.match(/^[^\(\[]+/); return yield module.exports.lookupId(response.body.data[0].id, type);
if (matches && matches[0] && matches[0] != album) { } else {
var cleanedData = JSON.parse(JSON.stringify(data)); var matches = album.match(/^[^\(\[]+/);
if (type == "album") { if (matches && matches[0] && matches[0] !== album) {
cleanedData.name = matches[0].trim(); var cleanedData = JSON.parse(JSON.stringify(data));
} else if (type == "track") { if (type === 'album') {
cleanedData.album.name = matches[0].trim(); cleanedData.name = matches[0].trim();
} } else if (type === 'track') {
return module.exports.search(cleanedData); cleanedData.album.name = matches[0].trim();
} else {
return {service: "deezer"};
} }
return yield module.exports.search(cleanedData);
} else { } else {
return module.exports.lookupId(res.body.data[0].id, type); return Promise.resolve({service: 'deezer'});
} }
}); }
}; };

View file

@ -1,138 +1,134 @@
"use strict"; import {parse} from 'url';
var parse = require("url").parse; import querystring from 'querystring';
var Promise = require('bluebird'); import request from 'superagent';
var querystring = require('querystring'); import 'superagent-bluebird-promise';
var request = require('superagent');
require('superagent-bluebird-promise');
module.exports.id = "itunes"; module.exports.id = 'itunes';
var apiRoot = "https://itunes.apple.com"; const apiRoot = 'https://itunes.apple.com';
module.exports.match = require('./url').match; module.exports.match = require('./url').match;
module.exports.parseUrl = function(url) { module.exports.parseUrl = function* (url) {
var parsed = parse(url); let parsed = parse(url);
var matches = parsed.path.match(/[\/]?([\/]?[a-z]{2}?)?[\/]+album[\/]+([^\/]+)[\/]+([^\?]+)/); let matches = parsed.path.match(/[\/]?([\/]?[a-z]{2}?)?[\/]+album[\/]+([^\/]+)[\/]+([^\?]+)/);
var query = querystring.parse(parsed.query); let query = querystring.parse(parsed.query);
if (matches) { if (matches) {
var type = "album"; let type = 'album';
var id = matches[3].substr(2); let id = matches[3].substr(2);
if (query.i) { if (query.i) {
type = "track"; type = 'track';
id = query.i; id = query.i;
} }
return module.exports.lookupId(id, type, matches[1] || "us"); return yield module.exports.lookupId(id, type, matches[1] || 'us');
} else { } else {
throw new Error(); return Promise.reject(new Error());
} }
}; };
module.exports.lookupId = function(id, type, cc) { module.exports.lookupId = function* (id, type, cc) {
if (String(id).match(/^[a-z]{2}/)) { if (String(id).match(/^[a-z]{2}/)) {
cc = id.substr(0,2); cc = id.substr(0, 2);
id = id.substr(2); id = id.substr(2);
} }
var path = "/lookup?id=" + id; let path = '/lookup?id=' + id;
if (cc) { if (cc) {
path = "/" + cc + path; path = '/' + cc + path;
} }
return request.get(apiRoot + path).promise().then(function(res) { let response = yield request.get(apiRoot + path);
var data = JSON.parse(res.text); let result = JSON.parse(response.text);
if (!data.results || data.resultCount == 0 || !data.results[0].collectionId) { if (!result.results || result.resultCount === 0 || !result.results[0].collectionId) {
var error = new Error("Not Found"); let error = new Error('Not Found');
error.status = 404; error.status = 404;
throw error; return Promise.reject(error);
} else { } else {
var result = data.results[0]; result = result.results[0];
var item = { let item = {
service: "itunes", service: 'itunes',
type: type, type: type,
id: cc + id, id: cc + id,
name: result.trackName ? result.trackName : result.collectionName, name: result.trackName ? result.trackName : result.collectionName,
streamUrl: null, streamUrl: null,
purchaseUrl: result.collectionViewUrl, purchaseUrl: result.collectionViewUrl,
artwork: { artwork: {
small: "https://match.audio/itunes/" + result.artworkUrl100.replace("100x100", "200x200").replace("http://", ""), small: 'https://match.audio/itunes/' + result.artworkUrl100.replace('100x100', '200x200').replace('http://', ''),
large: "https://match.audio/itunes/" + result.artworkUrl100.replace("100x100", "600x600").replace("http://", ""), large: 'https://match.audio/itunes/' + result.artworkUrl100.replace('100x100', '600x600').replace('http://', '')
}, },
artist: { artist: {
name: result.artistName name: result.artistName
}
};
if (type == "track") {
item.album = {
name: result.collectionName
};
} }
};
return item; if (type === 'track') {
item.album = {
name: result.collectionName
};
} }
});
return Promise.resolve(item);
}
}; };
module.exports.search = function(data) { module.exports.search = function* (data) {
var query, album, entity; let query, album, entity;
var type = data.type; let type = data.type;
if (type == "album") { if (type === 'album') {
query = data.artist.name + " " + data.name; query = data.artist.name + ' ' + data.name;
album = data.name; album = data.name;
entity = "album"; entity = 'album';
} else if (type == "track") { } else if (type === 'track') {
query = data.artist.name + " " + data.album.name + " " + data.name; query = data.artist.name + ' ' + data.album.name + ' ' + data.name;
album = data.album.name; album = data.album.name;
entity = "musicTrack"; entity = 'musicTrack';
} }
var path = "/search?term=" + encodeURIComponent(query) + "&media=music&entity=" + entity; let path = '/search?term=' + encodeURIComponent(query) + '&media=music&entity=' + entity;
return request.get(apiRoot + path).promise().then(function(res) { let response = yield request.get(apiRoot + path);
var result = JSON.parse(res.text); let result = JSON.parse(response.text);
if (!result.results[0]) { if (!result.results[0]) {
var matches = album.match(/^[^\(\[]+/); let matches = album.match(/^[^\(\[]+/);
if (matches && matches[0] && matches[0] != album) { if (matches && matches[0] && matches[0] !== album) {
var cleanedData = JSON.parse(JSON.stringify(data)); let cleanedData = JSON.parse(JSON.stringify(data));
if (type == "album") { if (type === 'album') {
cleanedData.name = matches[0].trim(); cleanedData.name = matches[0].trim();
} else if (type == "track") { } else if (type === 'track') {
cleanedData.album.name = matches[0].trim(); cleanedData.album.name = matches[0].trim();
}
return module.exports.search(cleanedData);
} else {
return {service: "itunes"};
} }
return yield module.exports.search(cleanedData);
} else { } else {
var result = result.results[0]; return Promise.resolve({service: 'itunes'});
var item = {
service: "itunes",
type: type,
id: "us" + result.collectionId,
name: result.trackName ? result.trackName : result.collectionName,
streamUrl: null,
purchaseUrl: result.collectionViewUrl,
artwork: {
small: "https://match.audio/itunes/" + result.artworkUrl100.replace("100x100", "200x200").replace("http://", ""),
large: "https://match.audio/itunes/" + result.artworkUrl100.replace("100x100", "600x600").replace("http://", ""),
},
artist: {
name: result.artistName
}
};
if (type == "track") {
item.album = {
name: result.collectionName
};
}
return item;
} }
}); } else {
result = result.results[0];
let item = {
service: 'itunes',
type: type,
id: 'us' + result.collectionId,
name: result.trackName ? result.trackName : result.collectionName,
streamUrl: null,
purchaseUrl: result.collectionViewUrl,
artwork: {
small: 'https://match.audio/itunes/' + result.artworkUrl100.replace('100x100', '200x200').replace('http://', ''),
large: 'https://match.audio/itunes/' + result.artworkUrl100.replace('100x100', '600x600').replace('http://', '')
},
artist: {
name: result.artistName
}
};
if (type === 'track') {
item.album = {
name: result.collectionName
};
}
return Promise.resolve(item);
}
}; };

View file

@ -6,8 +6,7 @@ module.exports.id = "rdio";
if (!process.env.RDIO_API_KEY || !process.env.RDIO_API_SHARED) { if (!process.env.RDIO_API_KEY || !process.env.RDIO_API_SHARED) {
console.warn("RDIO_API_KEY or RDIO_API_SHARED environment variables not found, deactivating Rdio."); console.warn("RDIO_API_KEY or RDIO_API_SHARED environment variables not found, deactivating Rdio.");
return; } else {
}
var rdio = require('rdio')({ var rdio = require('rdio')({
rdio_api_key: process.env.RDIO_API_KEY, rdio_api_key: process.env.RDIO_API_KEY,
@ -178,3 +177,5 @@ module.exports.search = function(data) {
} }
}); });
}; };
}

View file

@ -8,8 +8,7 @@ module.exports.id = "xbox";
if (!process.env.XBOX_CLIENT_ID || !process.env.XBOX_CLIENT_SECRET) { if (!process.env.XBOX_CLIENT_ID || !process.env.XBOX_CLIENT_SECRET) {
console.warn("XBOX_CLIENT_ID and XBOX_CLIENT_SECRET environment variables not found, deactivating Xbox Music."); console.warn("XBOX_CLIENT_ID and XBOX_CLIENT_SECRET environment variables not found, deactivating Xbox Music.");
return; } else {
}
var credentials = { var credentials = {
clientId: process.env.XBOX_CLIENT_ID, clientId: process.env.XBOX_CLIENT_ID,
@ -104,3 +103,5 @@ module.exports.search = function(data) {
}); });
}); });
}; };
}

View file

@ -11,8 +11,7 @@ module.exports.id = "youtube";
if (!process.env.YOUTUBE_KEY) { if (!process.env.YOUTUBE_KEY) {
console.warn("YOUTUBE_KEY environment variable not found, deactivating Youtube."); console.warn("YOUTUBE_KEY environment variable not found, deactivating Youtube.");
return; } else {
}
var credentials = { var credentials = {
key: process.env.YOUTUBE_KEY, key: process.env.YOUTUBE_KEY,
@ -136,3 +135,5 @@ module.exports.search = function(data) {
return {service: "youtube"}; return {service: "youtube"};
}); });
}; };
}

View file

@ -3,13 +3,13 @@
"version": "0.0.0", "version": "0.0.0",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "node ./bin/www", "start": "babel-node app.js",
"test": "./node_modules/mocha/bin/mocha test/**/*.js --timeout=10000", "test": "mocha --require co-mocha --compilers js:babel/register test/**/*.js --timeout=10000",
"build": "browserify ./views/app.jsx | uglifyjs -cm 2>/dev/null > ./public/javascript/bundle.js", "build": "browserify -t babelify ./views/app.jsx > ./public/javascript/bundle.js",
"clean": "rm -f ./public/javascript/bundle.js" "clean": "rm -f ./public/javascript/bundle.js"
}, },
"engines": { "engines": {
"node": "0.12.x" "iojs": "2.2.1"
}, },
"browserify": { "browserify": {
"transform": [ "transform": [
@ -22,38 +22,39 @@
] ]
}, },
"dependencies": { "dependencies": {
"bluebird": "^2.4.2", "babel": "^5.2.17",
"body-parser": "~1.10.0", "babelify": "^6.0.2",
"browserify": "^7.0.0", "bluebird": "^2.9.25",
"compression": "^1.2.2", "browserify": "^10.1.3",
"connect-browserify": "^3.2.1", "co": "^4.5.4",
"connect-flash": "^0.1.1", "debug": "^2.1.1",
"cookie-parser": "~1.3.3", "koa": "^0.21.0",
"crypto-js": "^3.1.2-5", "koa-bodyparser": "^2.0.0",
"debug": "~2.1.0", "koa-compress": "^1.0.8",
"envify": "^3.2.0", "koa-favicon": "^1.2.0",
"express": "~4.10.6", "koa-logger": "^1.2.2",
"express-session": "^1.9.2", "koa-route": "^2.4.0",
"helmet": "^0.5.2", "koa-static": "^1.4.9",
"moment": "^2.9.0", "moment": "^2.10.3",
"morgan": "~1.5.0", "mongodb-promisified": "^1.0.2",
"node-jsx": "^0.12.4",
"node-uuid": "^1.4.2", "node-uuid": "^1.4.2",
"playmusic": "^2.0.2", "playmusic": "^2.0.0",
"promised-mongo": "^0.11.1",
"rdio": "^1.5.2", "rdio": "^1.5.2",
"react": "^0.12.1", "react": "^0.13.3",
"react-google-analytics": "^0.2.0", "react-google-analytics": "^0.2.0",
"react-router": "^0.11.6", "react-router": "^0.13.3",
"reactify": "^0.17.1", "reactify": "^1.1.1",
"serve-favicon": "~2.2.0",
"spotify": "^0.3.0", "spotify": "^0.3.0",
"superagent": "^0.21.0", "superagent": "^1.2.0",
"superagent-bluebird-promise": "^0.5.1", "superagent-bluebird-promise": "^2.0.2"
"uglify-js": "^2.4.16"
}, },
"devDependencies": { "devDependencies": {
"should": "^4.4.1", "co-mocha": "^1.1.0",
"mocha": "^2.0.1" "eslint": "^0.22.1",
"eslint-plugin-react": "^2.5.0",
"mocha": "^2.1.0",
"nodemon": "^1.3.7",
"parallelshell": "^1.1.1",
"should": "^6.0.3"
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

24968
public/javascript/bundle.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,30 +1,28 @@
"use strict"; 'use strict';
var React = require("react"); import React from 'react';
var Router = require("react-router"); import createHandler from '../lib/react-handler';
require("node-jsx").install({extension: ".jsx"}); import {routes} from '../views/app.jsx';
var routes = require("../views/app.jsx").routes;
module.exports = function(req, res, next) { module.exports = function* () {
let recents = [];
req.db.matches.find().sort({"created_at": -1}).limit(6).toArray().then(function(docs){ let docs = yield this.db.matches.find().sort({'created_at': -1}).limit(6).toArray();
var recents = []; docs.forEach(function(doc) {
docs.forEach(function(doc) { let shares = Object.keys(doc.services).map(function (key) {return doc.services[key]; });
var shares = Object.keys(doc.services).map(function (key) {return doc.services[key]; }); shares.some(function(item) {
shares.some(function(item) { if (item.service === doc._id.split('$$')[0]) { // eslint-disable-line no-underscore-dangle
if (item.service === doc._id.split("$$")[0]) { // eslint-disable-line no-underscore-dangle recents.push(item);
recents.push(item); return false;
return false; }
}
});
}); });
Router.run(routes, req.url, function (Handler) {
var App = React.createFactory(Handler);
var content = React.renderToString(new App({recents: recents}));
res.send("<!doctype html>\n" + content.replace("</body></html>", "<script>var recents = " + JSON.stringify(recents) + "</script></body></html>"));
});
}).catch(function(error) {
next(error);
}); });
let Handler = yield createHandler(routes, this.request.url);
let App = React.createFactory(Handler);
let content = React.renderToString(new App({recents: recents}));
content = content.replace('</body></html>', '<script>var recents = ' + JSON.stringify(recents) + '</script></body></html>');
this.body = '<!doctype html>\n' + content;
}; };

View file

@ -1,14 +1,14 @@
"use strict"; import {parse} from 'url';
var parse = require("url").parse; import request from 'superagent';
var request = require("superagent");
module.exports = function(req, res) { module.exports = function* (next) {
var url = "http://" + req.url.substr(8); let url = 'http://' + this.request.url.substr(8);
var parsed = parse(url); let parsed = parse(url);
if (parsed.host.match(/mzstatic\.com/)) { if (parsed.host.match(/mzstatic\.com/)) {
request.get(url, function(response){ let proxyResponse = yield request.get(url);
res.set(response.headers); this.set(proxyResponse.headers);
res.send(response.body); this.body = proxyResponse.body;
}); } else {
yield next;
} }
}; };

View file

@ -1,44 +1,42 @@
"use strict"; import {parse} from 'url';
var parse = require("url").parse; import co from 'co';
var lookup = require("../lib/lookup"); import lookup from '../lib/lookup';
var services = require("../lib/services"); import services from '../lib/services';
module.exports = function(req, res) { module.exports = function* () {
var url = parse(req.body.url); let url = parse(this.request.body.url);
if (!url.host) { this.assert(url.host, 400, {error: {message: 'You need to submit a url.'}});
return res.json({error: {message: "You need to submit a url."}});
}
var promise = lookup(req.body.url); let item = yield lookup(this.request.body.url);
if (!promise) { this.assert(item, 400, {error: {message: 'No supported music found at that link :('}});
return res.json({error: {message: "No supported music found at that link :("}});
}
promise.then(function(item) { item.matched_at = new Date(); // eslint-disable-line camelcase
if (!item) { let matches = {};
return res.json({error: {message: "No supported music found at that link :("}}); matches[item.service] = item;
for (let service of services) {
if (service.id === item.service) {
continue;
} }
item.matched_at = new Date(); // eslint-disable-line camelcase matches[service.id] = {service: service.id};
var matches = {}; }
matches[item.service] = item;
services.forEach(function(service) { yield this.db.matches.save({_id: item.service + '$$' + item.id, 'created_at': new Date(), services: matches});
this.body = item;
process.nextTick(co.wrap(function* (){
for (let service of services) {
if (service.id === item.service) { if (service.id === item.service) {
return; continue;
} }
matches[service.id] = {service: service.id}; matches[service.id] = {service: service.id};
service.search(item).then(function(match) { let match = yield service.search(item);
match.matched_at = new Date(); // eslint-disable-line camelcase match.matched_at = new Date(); // eslint-disable-line camelcase
var update = {}; let update = {};
update["services." + match.service] = match; update['services.' + match.service] = match;
req.db.matches.update({_id: item.service + "$$" + item.id}, {"$set": update}); yield this.db.matches.updateOne({_id: item.service + '$$' + item.id}, {'$set': update});
}); }
}); }.bind(this)));
return req.db.matches.save({_id: item.service + "$$" + item.id, "created_at": new Date(), services: matches}).then(function() {
res.json(item);
});
}, function(error) {
console.log(error.stack);
res.json({error: {message: "No matches found for this link, sorry :("}});
});
}; };

View file

@ -1,13 +1,9 @@
"use strict"; import React from 'react';
import createHandler from '../lib/react-handler';
import {routes} from '../views/app.jsx';
import services from '../lib/services';
var React = require("react"); let formatAndSort = function(matches, serviceId) {
var Router = require("react-router");
require("node-jsx").install();
var routes = require("../views/app.jsx").routes;
var services = require("../lib/services");
var formatAndSort = function(matches, serviceId) {
matches = Object.keys(matches).map(function (key) {return matches[key]; }); matches = Object.keys(matches).map(function (key) {return matches[key]; });
matches.sort(function(a, b) { matches.sort(function(a, b) {
return a.id && !b.id; return a.id && !b.id;
@ -17,60 +13,34 @@ var formatAndSort = function(matches, serviceId) {
return matches; return matches;
}; };
module.exports = function(req, res, next) { module.exports = function* (serviceId, type, itemId, format, next) {
var serviceId = req.params.service; let matchedService;
var type = req.params.type;
var itemId = req.params.id;
var matchedService;
services.some(function(service) { services.some(function(service) {
matchedService = serviceId === service.id ? service : null; matchedService = serviceId === service.id ? service : null;
return matchedService; return matchedService;
}); });
if (!matchedService || (type !== "album" && type !== "track")) { if (!matchedService || (type !== 'album' && type !== 'track')) {
return next(); return yield next;
} }
return req.db.matches.findOne({_id: serviceId + "$$" + itemId}).then(function(doc) { let shares = [];
if (!doc) { let doc = yield this.db.matches.findOne({_id: serviceId + '$$' + itemId});
return matchedService.lookupId(itemId, type).then(function(item) {
var matches = {};
item.matched_at = new Date(); // eslint-disable-line camelcase
matches[item.service] = item;
services.forEach(function(service) {
if (service.id === item.service) {
return;
}
matches[service.id] = {service: service.id};
service.search(item).then(function(match) {
match.matched_at = new Date(); // eslint-disable-line camelcase
var update = {};
update["services." + match.service] = match;
req.db.matches.update({_id: item.service + "$$" + item.id}, {"$set": update});
});
});
return req.db.matches.save({_id: item.service + "$$" + item.id, "created_at": new Date(), services: matches}).then(function() {
var newShares = formatAndSort(matches, serviceId);
Router.run(routes, req.url, function (Handler) {
var App = React.createFactory(Handler);
var content = React.renderToString(new App({shares: newShares}));
res.send("<!doctype html>\n" + content.replace("</body></html>", "<script>var shares = " + JSON.stringify(newShares) + "</script></body></html>"));
});
});
});
}
var shares = formatAndSort(doc.services, serviceId);
if (req.params.format === "json") {
return res.json({shares: shares});
}
Router.run(routes, req.url, function (Handler) {
var App = React.createFactory(Handler);
var content = React.renderToString(new App({shares: shares}));
res.send("<!doctype html>\n" + content.replace("</body></html>", "<script>var shares = " + JSON.stringify(shares) + "</script></body></html>"));
});
}).catch(function (error) {
return next(error);
});
this.assert(doc, 404, 'Not Found');
shares = formatAndSort(doc.services, serviceId);
if (format === 'json') {
this.body = {shares: shares};
} else {
let Handler = yield createHandler(routes, this.request.url);
let App = React.createFactory(Handler);
let content = React.renderToString(new App({shares: shares}));
content = content.replace('</body></html>', '<script>var shares = ' + JSON.stringify(shares) + '</script></body></html>');
this.body = '<!doctype html>\n' + content;
}
}; };

6
test/.eslintrc Normal file
View file

@ -0,0 +1,6 @@
{
"env": {
"node": true,
"mocha": true
}
}

View file

@ -1,15 +1,9 @@
"use strict"; import 'should';
require('should'); import lookup from '../lib/lookup';
var lookup = require("../lib/lookup");
describe('Search with url', function(){ describe('Search with url', function(){
it('should find album by url', function* (){
it('should find album by url', function(done){ let result = yield lookup('https://play.google.com/music/listen#/album/Bz6wrjczddcj5hurijsv6ohdoay');
lookup("https://play.google.com/music/listen#/album/Bz6wrjczddcj5hurijsv6ohdoay").then(function(result) { result.name.should.equal('Phase 5');
result.name.should.equal("Phase 5");
done();
});
}); });
}); });

View file

@ -1,57 +0,0 @@
"use strict";
var assert = require("assert");
var should = require('should');
var beats = require("../../lib/services/beats");
describe('Beats Music', function(){
describe('lookupId', function(){
it('should find album by ID', function(done){
beats.lookupId("al920431", "album").then(function(result) {
result.name.should.equal("Deftones");
done();
});
});
it('should find track by ID', function(done){
beats.lookupId("tr6910289", "track").then(function(result) {
result.name.should.equal("Californication");
done();
});
});
});
describe('search', function(){
it('should find album by search', function(done){
beats.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){
beats.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){
beats.parseUrl("https://listen.beatsmusic.com/albums/al920431").then(function(result) {
result.id.should.equal("al920431");
done();
});
});
it('should parse track url into ID', function(done){
beats.parseUrl("https://listen.beatsmusic.com/albums/al6910269/tracks/tr6910289").then(function(result) {
result.id.should.equal("tr6910289");
done();
});
});
});
});
});

View file

@ -1,56 +1,41 @@
"use strict"; import 'should';
var assert = require("assert"); import deezer from '../../lib/services/deezer';
var should = require('should');
var deezer = require("../../lib/services/deezer");
describe('Deezer', function(){ describe('Deezer', function(){
describe('lookupId', function(){ describe('lookupId', function(){
it('should find album by ID', function(done){ it('should find album by ID', function* (){
deezer.lookupId("302127", "album").then(function(result) { let result = yield deezer.lookupId('302127', 'album');
result.name.should.equal("Discovery"); result.name.should.equal('Discovery');
done();
});
}); });
it('should find track by ID', function(done){ it('should find track by ID', function* (){
deezer.lookupId("3135554", "track").then(function(result) { let result = yield deezer.lookupId('3135554', 'track');
result.name.should.equal("Aerodynamic"); result.name.should.equal('Aerodynamic');
done();
});
}); });
}); });
describe('search', function(){ describe('search', function(){
it('should find album by search', function(done){ it('should find album by search', function* (){
deezer.search({type: "album", artist: {name: "David Guetta"}, name: "Listen (Deluxe)"}).then(function(result) { let result = yield deezer.search({type: 'album', artist: {name: 'David Guetta'}, name: 'Listen (Deluxe)'});
result.name.should.startWith("Listen"); result.name.should.startWith('Listen');
done();
});
}); });
it('should find track by search', function(done){ it('should find track by search', function* (){
deezer.search({type: "track", artist: {name: "Deftones"}, album: {name: "Deftones"}, name: "Hexagram"}).then(function(result) { let result = yield deezer.search({type: 'track', artist: {name: 'Deftones'}, album: {name: 'Deftones'}, name: 'Hexagram'});
result.name.should.equal("Hexagram"); result.name.should.equal('Hexagram');
done();
});
}); });
}); });
describe('lookupUrl', function(){ describe('lookupUrl', function(){
describe('parseUrl', function(){ describe('parseUrl', function(){
it('should parse album url into ID', function(done){ it('should parse album url into ID', function* (){
deezer.parseUrl("http://www.deezer.com/album/302127").then(function(result) { let result = yield deezer.parseUrl('http://www.deezer.com/album/302127');
result.id.should.equal(302127); result.id.should.equal(302127);
done();
});
}); });
it('should parse track url into ID', function(done){ it('should parse track url into ID', function* (){
deezer.parseUrl("http://www.deezer.com/track/3135554").then(function(result) { let result = yield deezer.parseUrl('http://www.deezer.com/track/3135554');
result.id.should.equal(3135554); result.id.should.equal(3135554);
done();
});
}); });
}); });
}); });

View file

@ -1,55 +1,40 @@
"use strict"; import 'should';
var assert = require("assert"); import google from '../../lib/services/google';
var should = require('should');
var google = require("../../lib/services/google");
describe('Google Play Music', function(){ describe('Google Play Music', function(){
describe('lookupId', function(){ describe('lookupId', function(){
it('should find album by ID', function(done){ it('should find album by ID', function* (){
google.lookupId("Byp6lvzimyf74wxi5634ul4tgam", "album").then(function(result) { let result = yield google.lookupId('Byp6lvzimyf74wxi5634ul4tgam', 'album');
result.name.should.equal("Listen (Deluxe)"); result.name.should.equal('Listen (Deluxe)');
done();
});
}); });
it('should find track by ID', function(done){ it('should find track by ID', function* (){
google.lookupId("Tjosptub24g2dft37lforqnudpe", "track").then(function(result) { let result = yield google.lookupId('Tjosptub24g2dft37lforqnudpe', 'track');
result.name.should.equal("Cherub Rock"); result.name.should.equal('Cherub Rock');
done();
});
}); });
}); });
describe('search', function(){ describe('search', function(){
it('should find album by search', function(done){ it('should find album by search', function* (){
google.search({type: "album", artist: {name: "David Guetta"}, name: "Listen (Deluxe)"}).then(function(result) { let result = yield google.search({type: 'album', artist: {name: 'David Guetta'}, name: 'Listen (Deluxe)'});
result.name.should.equal("Listen (Deluxe)"); result.name.should.equal('Listen (Deluxe)');
done();
});
}); });
}); });
describe('lookupUrl', function(){ describe('lookupUrl', function(){
it('should parse regular url into album ID', function(done){ it('should parse regular url into album ID', function* (){
google.parseUrl("https://play.google.com/music/listen#/album/Byp6lvzimyf74wxi5634ul4tgam/David+Guetta/Listen+(Deluxe)").then(function(result) { let result = yield google.parseUrl('https://play.google.com/music/listen#/album/Byp6lvzimyf74wxi5634ul4tgam/David+Guetta/Listen+(Deluxe)');
result.id.should.equal("Byp6lvzimyf74wxi5634ul4tgam"); result.id.should.equal('Byp6lvzimyf74wxi5634ul4tgam');
done();
});
}); });
it('should parse url without ID into album ID', function(done){ it('should parse url without ID into album ID', function* (){
google.parseUrl("https://play.google.com/music/listen#/album//David+Guetta/Listen+(Deluxe)").then(function(result) { let result = yield google.parseUrl('https://play.google.com/music/listen#/album//David+Guetta/Listen+(Deluxe)');
result.id.should.equal("Byp6lvzimyf74wxi5634ul4tgam"); result.id.should.equal('Byp6lvzimyf74wxi5634ul4tgam');
done();
});
}); });
it('should parse share url into album ID', function(done){ it('should parse share url into album ID', function* (){
google.parseUrl("https://play.google.com/music/m/Byp6lvzimyf74wxi5634ul4tgam").then(function(result) { let result = yield google.parseUrl('https://play.google.com/music/m/Byp6lvzimyf74wxi5634ul4tgam');
result.id.should.equal("Byp6lvzimyf74wxi5634ul4tgam"); result.id.should.equal('Byp6lvzimyf74wxi5634ul4tgam');
done();
});
}); });
}); });
}); });

View file

@ -1,56 +1,41 @@
"use strict"; import 'should';
var assert = require("assert"); import itunes from '../../lib/services/itunes';
var should = require('should');
var itunes = require("../../lib/services/itunes");
describe('iTunes Music', function(){ describe('iTunes Music', function(){
describe('lookupId', function(){ describe('lookupId', function(){
it('should find album by ID', function(done){ it('should find album by ID', function* (){
itunes.lookupId("id215206912", "album").then(function(result) { let result = yield itunes.lookupId('id215206912', 'album');
result.name.should.equal("Peace Orchestra"); result.name.should.equal('Peace Orchestra');
done();
});
}); });
it('should find track by ID', function(done){ it('should find track by ID', function* (){
itunes.lookupId("id215206958", "track").then(function(result) { let result = yield itunes.lookupId('id215206958', 'track');
result.name.should.equal("Double Drums"); result.name.should.equal('Double Drums');
done();
});
}); });
}); });
describe('search', function(){ describe('search', function(){
it('should find album by search', function(done){ it('should find album by search', function* (){
itunes.search({type: "album", artist: {name: "Deftones"}, name: "Deftones"}).then(function(result) { let result = yield itunes.search({type: 'album', artist: {name: 'Deftones'}, name: 'Deftones'});
result.name.should.equal("Deftones"); result.name.should.equal('Deftones');
done();
});
}); });
it('should find track by search', function(done){ it('should find track by search', function* (){
itunes.search({type: "track", artist: {name: "Deftones"}, album: {name: "Deftones"}, name: "Hexagram"}).then(function(result) { let result = yield itunes.search({type: 'track', artist: {name: 'Deftones'}, album: {name: 'Deftones'}, name: 'Hexagram'});
result.name.should.equal("Hexagram"); result.name.should.equal('Hexagram');
done();
});
}); });
}); });
describe('lookupUrl', function(){ describe('lookupUrl', function(){
describe('parseUrl', function(){ describe('parseUrl', function(){
it('should parse album url into ID', function(done){ it('should parse album url into ID', function* (){
itunes.parseUrl("https://itunes.apple.com/us/album/double-drums/id215206912").then(function(result) { let result = yield itunes.parseUrl('https://itunes.apple.com/us/album/double-drums/id215206912');
result.id.should.equal("us215206912"); result.id.should.equal('us215206912');
done();
});
}); });
it('should parse track url into ID', function(done){ it('should parse track url into ID', function* (){
itunes.parseUrl("https://itunes.apple.com/us/album/double-drums/id215206912?i=215206958&uo=4").then(function(result) { let result = yield itunes.parseUrl('https://itunes.apple.com/us/album/double-drums/id215206912?i=215206958&uo=4');
result.id.should.equal("us215206958"); result.id.should.equal('us215206958');
done();
});
}); });
}); });
}); });

View file

@ -1,41 +1,30 @@
"use strict"; import 'should';
var assert = require("assert"); import rdio from '../../lib/services/rdio';
var should = require('should');
var rdio = require("../../lib/services/rdio");
describe('Rdio', function(){ describe('Rdio', function(){
describe('lookupId', function(){ describe('lookupId', function(){
it('should find album by ID', function(done){ it('should find album by ID', function* (){
rdio.lookupId("Qj4NXr0").then(function(result) { let result = yield rdio.lookupId('Qj4NXr0');
result.name.should.equal("Listen (Deluxe)"); result.name.should.equal('Listen (Deluxe)');
done();
});
}); });
}); });
describe('search', function(){ describe('search', function(){
it('should find album by search', function(done){ it('should find album by search', function* (){
rdio.search({type: "album", artist: {name: "David Guetta"}, name: "Listen (Deluxe)"}).then(function(result) { let result = yield rdio.search({type: 'album', artist: {name: 'David Guetta'}, name: 'Listen (Deluxe)'});
result.name.should.equal("Listen (Deluxe)"); result.name.should.equal('Listen (Deluxe)');
done();
});
}); });
}); });
describe('parseUrl', function(){ describe('parseUrl', function(){
it('should parse regular url into album object', function(done){ it('should parse regular url into album object', function* (){
rdio.parseUrl("https://www.rdio.com/artist/David_Guetta/album/Listen_(Deluxe)/").then(function(result) { let result = yield rdio.parseUrl('https://www.rdio.com/artist/David_Guetta/album/Listen_(Deluxe)/');
result.name.should.equal("Listen (Deluxe)"); result.name.should.equal('Listen (Deluxe)');
done();
});
}); });
it('should parse short url into album object', function(done){ it('should parse short url into album object', function* (){
rdio.parseUrl("http://rd.io/x/Qj4NXr0/").then(function(result) { let result = yield rdio.parseUrl('http://rd.io/x/Qj4NXr0/');
result.name.should.equal("Listen (Deluxe)"); result.name.should.equal('Listen (Deluxe)');
done();
});
}); });
}); });
}); });

View file

@ -1,41 +1,30 @@
"use strict"; import 'should';
var assert = require("assert"); import spotify from '../../lib/services/spotify';
var should = require('should');
var spotify = require("../../lib/services/spotify");
describe('Spotify', function(){ describe('Spotify', function(){
describe('lookupId', function(){ describe('lookupId', function(){
it('should find album by ID', function(done){ it('should find album by ID', function* (){
spotify.lookupId("77UW17CZFyCaRLHdHeofZu", "album").then(function(result) { let result = yield spotify.lookupId('77UW17CZFyCaRLHdHeofZu', 'album');
result.name.should.equal("Listen (Deluxe)"); result.name.should.equal('Listen (Deluxe)');
done();
});
}); });
it('should find track by ID', function(done){ it('should find track by ID', function* (){
spotify.lookupId("7dS5EaCoMnN7DzlpT6aRn2", "track").then(function(result) { let result = yield spotify.lookupId('7dS5EaCoMnN7DzlpT6aRn2', 'track');
result.name.should.equal("Take Me To Church"); result.name.should.equal('Take Me To Church');
done();
});
}); });
}); });
describe('search', function(){ describe('search', function(){
it('should find album by search', function(done){ it('should find album by search', function* (){
spotify.search({type: "album", artist: {name: "David Guetta"}, name: "Listen (Deluxe)"}).then(function(result) { let result = yield spotify.search({type: 'album', artist: {name: 'David Guetta'}, name: 'Listen (Deluxe)'});
result.name.should.equal("Listen (Deluxe)"); result.name.should.equal('Listen (Deluxe)');
done();
});
}); });
}); });
describe('parseUrl', function(){ describe('parseUrl', function(){
it('should parse url into ID', function(done){ it('should parse url into ID', function* (){
spotify.parseUrl("https://play.spotify.com/album/77UW17CZFyCaRLHdHeofZu").then(function(result) { let result = yield spotify.parseUrl('https://play.spotify.com/album/77UW17CZFyCaRLHdHeofZu');
result.id.should.equal("77UW17CZFyCaRLHdHeofZu"); result.id.should.equal('77UW17CZFyCaRLHdHeofZu');
done();
});
}); });
}); });
}); });

View file

@ -1,41 +1,30 @@
"use strict"; import 'should';
var assert = require("assert"); import xbox from '../../lib/services/xbox';
var should = require('should');
var google = require("../../lib/services/xbox");
describe('Xbox Music', function(){ describe('Xbox Music', function(){
describe('lookupId', function(){ describe('lookupId', function(){
it('should find album by ID', function(done){ it('should find album by ID', function* (){
google.lookupId("music.8b558d00-0100-11db-89ca-0019b92a3933", "album").then(function(result) { let result = yield xbox.lookupId('music.8b558d00-0100-11db-89ca-0019b92a3933', 'album');
result.name.should.equal("Muchas Gracias: The Best Of Kyuss"); result.name.should.equal('Muchas Gracias: The Best Of Kyuss');
done();
});
}); });
it('should find track by ID', function(done){ it('should find track by ID', function* (){
google.lookupId("music.8f558d00-0100-11db-89ca-0019b92a3933", "track").then(function(result) { let result = yield xbox.lookupId('music.8f558d00-0100-11db-89ca-0019b92a3933', 'track');
result.name.should.equal("Shine"); result.name.should.equal('Shine');
done();
});
}); });
}); });
describe('search', function(){ describe('search', function(){
it('should find album by search', function(done){ it('should find album by search', function* (){
google.search({type: "album", artist: {name: "Kyuss"}, name: "Muchas Gracias: The Best Of Kyuss"}).then(function(result) { let result = yield xbox.search({type: 'album', artist: {name: 'Kyuss'}, name: 'Muchas Gracias: The Best Of Kyuss'});
result.name.should.equal("Muchas Gracias: The Best Of Kyuss"); result.name.should.equal('Muchas Gracias: The Best Of Kyuss');
done();
});
}); });
}); });
describe('lookupUrl', function(){ describe('lookupUrl', function(){
it('should parse regular url into album ID', function(done){ it('should parse regular url into album ID', function* (){
google.parseUrl("https://music.xbox.com/album/kyuss/muchas-gracias-the-best-of-kyuss/8b558d00-0100-11db-89ca-0019b92a3933").then(function(result) { let result = yield xbox.parseUrl('https://music.xbox.com/album/kyuss/muchas-gracias-the-best-of-kyuss/8b558d00-0100-11db-89ca-0019b92a3933');
result.id.should.equal("music.8B558D00-0100-11DB-89CA-0019B92A3933"); result.id.should.equal('music.8B558D00-0100-11DB-89CA-0019B92A3933');
done();
});
}); });
}); });
}); });

View file

@ -1,16 +1,11 @@
"use strict"; import 'should';
var assert = require("assert"); import youtube from '../../lib/services/youtube';
var should = require('should');
var youtube = require("../../lib/services/youtube");
describe('Youtube', function(){ describe('Youtube', function(){
describe('search', function(){ describe('search', function(){
it('should find album by search', function(done){ it('should find album by search', function* (){
youtube.search({type: "track", artist: {name: "Aesop Rock"}, album: {name: "Skelethon"}, name: "Zero Dark Thirty"}).then(function(result) { let result = yield youtube.search({type: 'track', artist: {name: 'Aesop Rock'}, album: {name: 'Skelethon'}, name: 'Zero Dark Thirty'});
result.name.should.equal("Aesop Rock - Zero Dark Thirty"); result.name.should.equal('Aesop Rock - Zero Dark Thirty');
done();
});
}); });
}); });
}); });

View file

@ -6,6 +6,7 @@
"react" "react"
], ],
"env": { "env": {
"browser": true "browser": true,
"es6": true
} }
} }

View file

@ -1,15 +1,15 @@
"use strict"; 'use strict';
var React = require("react"); var React = require('react');
var Router = require("react-router"); var Router = require('react-router');
var Route = require("react-router").Route; var Route = require('react-router').Route;
var DefaultRoute = require("react-router").DefaultRoute; var DefaultRoute = require('react-router').DefaultRoute;
var NotFoundRoute = require("react-router").NotFoundRoute; var NotFoundRoute = require('react-router').NotFoundRoute;
var RouteHandler = require("react-router").RouteHandler; var RouteHandler = require('react-router').RouteHandler;
var Home = require("./home.jsx"); var Home = require('./home.jsx');
var Share = require("./share.jsx"); var Share = require('./share.jsx');
var Head = require("./head.jsx"); var Head = require('./head.jsx');
var ga = require("react-google-analytics"); var ga = require('react-google-analytics');
var GAInitiailizer = ga.Initializer; var GAInitiailizer = ga.Initializer;
var App = React.createClass({ var App = React.createClass({
@ -17,42 +17,36 @@ var App = React.createClass({
return ( return (
<html> <html>
<Head {...this.props} /> <Head {...this.props} />
<body className="home"> <body className='home'>
<RouteHandler {...this.props} /> <RouteHandler {...this.props} />
<GAInitiailizer /> <GAInitiailizer />
<script src="/javascript/bundle.js" /> <script src='/javascript/bundle.js' />
</body> </body>
</html> </html>
); );
} }
}); });
var NotFound = React.createClass({
render: function () {
return <h2>Not found</h2>;
}
});
var routes = ( var routes = (
<Route name="home" handler={App} path="/"> <Route name='home' handler={App} path='/'>
<DefaultRoute handler={Home} /> <DefaultRoute handler={Home} />
<Route name="share" path=":service/:type/:id" handler={Share}/> <Route name='share' path=':service/:type/:id' handler={Share}/>
<NotFoundRoute handler={NotFound}/> <NotFoundRoute handler={Error}/>
</Route> </Route>
); );
module.exports.routes = routes; module.exports.routes = routes;
if (typeof window !== "undefined") { if (typeof window !== 'undefined') {
window.onload = function() { window.onload = function() {
Router.run(routes, Router.HistoryLocation, function (Handler) { Router.run(routes, Router.HistoryLocation, function (Handler) {
if (typeof window.recents !== "undefined") { if (typeof window.recents !== 'undefined') {
React.render(<Handler recents={window.recents} />, document); React.render(<Handler recents={window.recents} />, document);
} else if (typeof shares !== "undefined") { } else if (typeof shares !== 'undefined') {
React.render(<Handler shares={window.shares} />, document); React.render(<Handler shares={window.shares} />, document);
} }
}); });
ga("create", "UA-66209-8", "auto"); ga('create', 'UA-66209-8', 'auto');
ga("send", "pageview"); ga('send', 'pageview');
}; };
} }

View file

@ -1,13 +1,13 @@
"use strict"; 'use strict';
var React = require("react"); var React = require('react');
module.exports = React.createClass({ module.exports = React.createClass({
render: function() { render: function() {
return ( return (
<div className="row faq"> <div className='row faq'>
<div className="col-md-6 col-md-offset-3"> <div className='col-md-6 col-md-offset-3'>
<h2>Questions?</h2> <h2>Questions?</h2>
<ul> <ul>
<li> <li>
@ -20,11 +20,11 @@ module.exports = React.createClass({
</li> </li>
<li> <li>
<h3>Where do I find a link to paste in the box?</h3> <h3>Where do I find a link to paste in the box?</h3>
<p>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.</p> <p>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.</p>
</li> </li>
<li> <li>
<h3>Why don't you guys support Bandcamp, Amazon Music, Sony Music Unlimited&hellip; ?</h3> <h3>Why don't you guys support Bandcamp, Amazon Music, Sony Music Unlimited&hellip; ?</h3>
<p>Let me stop you there. <a href="https://github.com/kudos/match.audio">Match Audio is open source</a>, 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 <a href="https://github.com/kudos/match.audio/issues">submit a request</a> and maybe we'll do it for you.</p> <p>Let me stop you there. <a href='https://github.com/kudos/match.audio'>Match Audio is open source</a>, 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 <a href='https://github.com/kudos/match.audio/issues'>submit a request</a> and maybe we'll do it for you.</p>
</li> </li>
</ul> </ul>
</div> </div>

View file

@ -65,7 +65,7 @@ var SearchForm = React.createClass({
}); });
return; return;
} }
request.post("/search").send({url: url}).end(function(res) { request.post("/search").send({url: url}).end(function(req, res) {
that.setState({ that.setState({
submitting: false submitting: false
}); });
@ -118,7 +118,7 @@ module.exports = React.createClass({
componentDidMount: function () { componentDidMount: function () {
if (!this.props.recents) { if (!this.props.recents) {
request.get("/recent").set("Accept", "application/json").end(function(res) { request.get("/recent").set("Accept", "application/json").end(function(err, res) {
this.setState({ this.setState({
recents: res.body.recents recents: res.body.recents
}); });

View file

@ -100,7 +100,7 @@ module.exports = React.createClass({
}); });
var getShares = function() { var getShares = function() {
request.get(this.getPathname() + ".json").end(function(res) { request.get(this.getPathname() + ".json").end(function(err, res) {
var shares = res.body.shares; var shares = res.body.shares;
complete = true; complete = true;
shares.forEach(function(share) { shares.forEach(function(share) {