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": {
"node": true,
"es6": true

189
app.js
View file

@ -1,127 +1,102 @@
"use strict";
var express = require("express");
var helmet = require("helmet");
var path = require("path");
var favicon = require("serve-favicon");
var logger = require("morgan");
var session = require("express-session");
var cookieParser = require("cookie-parser");
var flash = require("connect-flash");
var compress = require("compression");
var bodyParser = require("body-parser");
var pmongo = require("promised-mongo");
import path from 'path';
import koa from 'koa';
import route from 'koa-route';
import logger from 'koa-logger';
import favicon from 'koa-favicon';
import compress from 'koa-compress';
import staticHandler from 'koa-static';
import bodyparser from 'koa-bodyparser';
import React from 'react';
import co from 'co';
import db from './config/db';
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");
var search = require("./routes/search");
var share = require("./routes/share");
var itunesProxy = require("./routes/itunes-proxy");
import debuglog from 'debug';
const debug = debuglog('match.audio');
var React = require("react");
require("node-jsx").install({extension: ".jsx"});
const app = koa();
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();
var development = process.env.NODE_ENV !== "production";
// 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();
this.body = '<!doctype html>\n' + content;
} else {
throw err;
}
}
});
if (development) {
app.get("/javascript/bundle.js",
browserify("./views/app.jsx", {
debug: true,
watch: true
}));
}
app.use(bodyparser());
app.use(compress({flush: zlib.Z_SYNC_FLUSH }));
app.use(favicon(path.join(__dirname, '/public/images/favicon.png')));
app.use(logger());
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
if (req.headers["cf-visitor"] && req.headers["cf-visitor"] !== "{\"scheme\":\"https\"}") {
return res.redirect("https://" + req.headers.host + req.url);
} else if (req.headers["cf-visitor"]) {
req.userProtocol = "https";
if (this.headers['cf-visitor'] && this.headers['cf-visitor'] !== '{"scheme":"https"}') {
return this.redirect('https://' + this.headers.host + this.url);
} else if (this.headers['cf-visitor']) {
this.userProtocol = 'https';
} else {
req.userProtocol = "http";
this.userProtocol = 'http';
}
// redirect www
if (req.headers.host.match(/^www/) !== null ) {
return res.redirect(req.userProtocol + "://" + req.headers.host.replace(/^www\./, "") + req.url);
if (this.headers.host.match(/^www/) !== null ) {
return this.redirect(this.userProtocol + '://' + this.headers.host.replace(/^www\./, '') + this.url);
} else {
next();
yield next;
}
});
app.get("/", index);
app.post("/search", search);
app.get("/:service/:type/:id.:format?", share);
app.get("/itunes/*", itunesProxy);
app.get("/recent", function(req, res, next) {
req.db.matches.find().sort({"created_at": -1}).limit(6).toArray().then(function(docs){
var recents = [];
docs.forEach(function(doc) {
recents.push(doc.services[doc._id.split("$$")[0]]); // eslint-disable-line no-underscore-dangle
});
res.json({recents: recents});
}).catch(function (error) {
return next(error);
app.use(route.get('/', index));
app.use(route.post('/search', search));
app.use(route.get('/itunes/(.*)', itunesProxy));
app.use(route.get('/:service/:type/:id.:format?', share));
app.use(route.get('/recent', function* () {
let recents = [];
let docs = yield this.db.matches.find().sort({'created_at': -1}).limit(6).toArray();
docs.forEach(function(doc) {
recents.push(doc.services[doc._id.split('$$')[0]]); // eslint-disable-line no-underscore-dangle
});
});
// 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);
});
this.body = {recents: recents};
}));
app.use(route.get('*', function* () {
this.throw(404);
}));
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
"use strict";
var debug = require('debug')('match.audio');
var app = require('../app');
var debug = require("debug")("match.audio");
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() {
debug('Express server listening on port ' + server.address().port);
var server = app.listen(app.get("port"), function() {
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";
var path = require("path");
import path from 'path';
import fs from 'fs';
var services = [];
require("fs").readdirSync(path.join(__dirname, "services")).forEach(function(file) {
var service = require(path.join(__dirname, "services", file));
fs.readdirSync(path.join(__dirname, 'services')).forEach(function(file) {
var service = require(path.join(__dirname, 'services', file));
if (service.search) {
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";
var path = require("path");
import path from 'path';
import fs from 'fs';
module.exports = [];
require("fs").readdirSync(path.join(__dirname, "services")).forEach(function(file) {
var service = require(path.join(__dirname, "services", file));
fs.readdirSync(path.join(__dirname, 'services')).forEach(function(file) {
var service = require(path.join(__dirname, 'services', file));
if (service.search) {
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";
var parse = require('url').parse;
var Promise = require('bluebird');
var request = require('superagent');
require('superagent-bluebird-promise');
import {parse} from 'url';
import request from 'superagent';
import '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.parseUrl = function(url) {
var matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/);
let matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/);
if (matches && matches[2]) {
return module.exports.lookupId(matches[2], matches[1]);
} else {
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) {
var cleanParam = function(str) {
return str.replace(/[\:\?\&]+/, "");
}
var query, album;
var type = data.type;
module.exports.lookupId = function* (id, type) {
let path = '/' + type + '/' + id;
if (type == "album") {
query = cleanParam(data.artist.name) + " " + cleanParam(data.name);
let {body} = yield request.get(apiRoot + path).promise();
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;
} else if (type == "track") {
query = cleanParam(data.artist.name) + " " + cleanParam(data.album.name) + " " + cleanParam(data.name);
} else if (type === 'track') {
query = cleanParam(data.artist.name) + ' ' + cleanParam(data.album.name) + ' ' + cleanParam(data.name);
album = data.album.name;
}
var path = "/search/" + type + "?q=" + encodeURIComponent(query);
return request.get(apiRoot + path).promise().then(function(res) {
if (!res.body.data[0]) {
var matches = album.match(/^[^\(\[]+/);
if (matches && matches[0] && matches[0] != album) {
var cleanedData = JSON.parse(JSON.stringify(data));
if (type == "album") {
cleanedData.name = matches[0].trim();
} else if (type == "track") {
cleanedData.album.name = matches[0].trim();
}
return module.exports.search(cleanedData);
} else {
return {service: "deezer"};
var path = '/search/' + type + '?q=' + encodeURIComponent(query);
let response = yield request.get(apiRoot + path);
if (response.body.data[0]) {
return yield module.exports.lookupId(response.body.data[0].id, type);
} else {
var matches = album.match(/^[^\(\[]+/);
if (matches && matches[0] && matches[0] !== album) {
var cleanedData = JSON.parse(JSON.stringify(data));
if (type === 'album') {
cleanedData.name = matches[0].trim();
} else if (type === 'track') {
cleanedData.album.name = matches[0].trim();
}
return yield module.exports.search(cleanedData);
} else {
return module.exports.lookupId(res.body.data[0].id, type);
return Promise.resolve({service: 'deezer'});
}
});
}
};

View file

@ -1,138 +1,134 @@
"use strict";
var parse = require("url").parse;
var Promise = require('bluebird');
var querystring = require('querystring');
var request = require('superagent');
require('superagent-bluebird-promise');
import {parse} from 'url';
import querystring from 'querystring';
import request from 'superagent';
import '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.parseUrl = function(url) {
var parsed = parse(url);
var matches = parsed.path.match(/[\/]?([\/]?[a-z]{2}?)?[\/]+album[\/]+([^\/]+)[\/]+([^\?]+)/);
var query = querystring.parse(parsed.query);
module.exports.parseUrl = function* (url) {
let parsed = parse(url);
let matches = parsed.path.match(/[\/]?([\/]?[a-z]{2}?)?[\/]+album[\/]+([^\/]+)[\/]+([^\?]+)/);
let query = querystring.parse(parsed.query);
if (matches) {
var type = "album";
var id = matches[3].substr(2);
let type = 'album';
let id = matches[3].substr(2);
if (query.i) {
type = "track";
type = 'track';
id = query.i;
}
return module.exports.lookupId(id, type, matches[1] || "us");
return yield module.exports.lookupId(id, type, matches[1] || 'us');
} 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}/)) {
cc = id.substr(0,2);
cc = id.substr(0, 2);
id = id.substr(2);
}
var path = "/lookup?id=" + id;
let path = '/lookup?id=' + id;
if (cc) {
path = "/" + cc + path;
path = '/' + cc + path;
}
return request.get(apiRoot + path).promise().then(function(res) {
var data = JSON.parse(res.text);
let response = yield request.get(apiRoot + path);
let result = JSON.parse(response.text);
if (!data.results || data.resultCount == 0 || !data.results[0].collectionId) {
var error = new Error("Not Found");
error.status = 404;
throw error;
} else {
var result = data.results[0];
if (!result.results || result.resultCount === 0 || !result.results[0].collectionId) {
let error = new Error('Not Found');
error.status = 404;
return Promise.reject(error);
} else {
result = result.results[0];
var item = {
service: "itunes",
type: type,
id: cc + id,
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
};
let item = {
service: 'itunes',
type: type,
id: cc + id,
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
}
};
return item;
if (type === 'track') {
item.album = {
name: result.collectionName
};
}
});
return Promise.resolve(item);
}
};
module.exports.search = function(data) {
var query, album, entity;
var type = data.type;
module.exports.search = function* (data) {
let query, album, entity;
let type = data.type;
if (type == "album") {
query = data.artist.name + " " + data.name;
if (type === 'album') {
query = data.artist.name + ' ' + data.name;
album = data.name;
entity = "album";
} else if (type == "track") {
query = data.artist.name + " " + data.album.name + " " + data.name;
entity = 'album';
} else if (type === 'track') {
query = data.artist.name + ' ' + data.album.name + ' ' + data.name;
album = data.album.name;
entity = "musicTrack";
entity = 'musicTrack';
}
var path = "/search?term=" + encodeURIComponent(query) + "&media=music&entity=" + entity;
return request.get(apiRoot + path).promise().then(function(res) {
var result = JSON.parse(res.text);
let path = '/search?term=' + encodeURIComponent(query) + '&media=music&entity=' + entity;
let response = yield request.get(apiRoot + path);
let result = JSON.parse(response.text);
if (!result.results[0]) {
var matches = album.match(/^[^\(\[]+/);
if (matches && matches[0] && matches[0] != album) {
var cleanedData = JSON.parse(JSON.stringify(data));
if (type == "album") {
cleanedData.name = matches[0].trim();
} else if (type == "track") {
cleanedData.album.name = matches[0].trim();
}
return module.exports.search(cleanedData);
} else {
return {service: "itunes"};
if (!result.results[0]) {
let matches = album.match(/^[^\(\[]+/);
if (matches && matches[0] && matches[0] !== album) {
let cleanedData = JSON.parse(JSON.stringify(data));
if (type === 'album') {
cleanedData.name = matches[0].trim();
} else if (type === 'track') {
cleanedData.album.name = matches[0].trim();
}
return yield module.exports.search(cleanedData);
} else {
var result = result.results[0];
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;
return Promise.resolve({service: 'itunes'});
}
});
} 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) {
console.warn("RDIO_API_KEY or RDIO_API_SHARED environment variables not found, deactivating Rdio.");
return;
}
} else {
var rdio = require('rdio')({
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) {
console.warn("XBOX_CLIENT_ID and XBOX_CLIENT_SECRET environment variables not found, deactivating Xbox Music.");
return;
}
} else {
var credentials = {
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) {
console.warn("YOUTUBE_KEY environment variable not found, deactivating Youtube.");
return;
}
} else {
var credentials = {
key: process.env.YOUTUBE_KEY,
@ -136,3 +135,5 @@ module.exports.search = function(data) {
return {service: "youtube"};
});
};
}

View file

@ -3,13 +3,13 @@
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www",
"test": "./node_modules/mocha/bin/mocha test/**/*.js --timeout=10000",
"build": "browserify ./views/app.jsx | uglifyjs -cm 2>/dev/null > ./public/javascript/bundle.js",
"start": "babel-node app.js",
"test": "mocha --require co-mocha --compilers js:babel/register test/**/*.js --timeout=10000",
"build": "browserify -t babelify ./views/app.jsx > ./public/javascript/bundle.js",
"clean": "rm -f ./public/javascript/bundle.js"
},
"engines": {
"node": "0.12.x"
"iojs": "2.2.1"
},
"browserify": {
"transform": [
@ -22,38 +22,39 @@
]
},
"dependencies": {
"bluebird": "^2.4.2",
"body-parser": "~1.10.0",
"browserify": "^7.0.0",
"compression": "^1.2.2",
"connect-browserify": "^3.2.1",
"connect-flash": "^0.1.1",
"cookie-parser": "~1.3.3",
"crypto-js": "^3.1.2-5",
"debug": "~2.1.0",
"envify": "^3.2.0",
"express": "~4.10.6",
"express-session": "^1.9.2",
"helmet": "^0.5.2",
"moment": "^2.9.0",
"morgan": "~1.5.0",
"node-jsx": "^0.12.4",
"babel": "^5.2.17",
"babelify": "^6.0.2",
"bluebird": "^2.9.25",
"browserify": "^10.1.3",
"co": "^4.5.4",
"debug": "^2.1.1",
"koa": "^0.21.0",
"koa-bodyparser": "^2.0.0",
"koa-compress": "^1.0.8",
"koa-favicon": "^1.2.0",
"koa-logger": "^1.2.2",
"koa-route": "^2.4.0",
"koa-static": "^1.4.9",
"moment": "^2.10.3",
"mongodb-promisified": "^1.0.2",
"node-uuid": "^1.4.2",
"playmusic": "^2.0.2",
"promised-mongo": "^0.11.1",
"playmusic": "^2.0.0",
"rdio": "^1.5.2",
"react": "^0.12.1",
"react": "^0.13.3",
"react-google-analytics": "^0.2.0",
"react-router": "^0.11.6",
"reactify": "^0.17.1",
"serve-favicon": "~2.2.0",
"react-router": "^0.13.3",
"reactify": "^1.1.1",
"spotify": "^0.3.0",
"superagent": "^0.21.0",
"superagent-bluebird-promise": "^0.5.1",
"uglify-js": "^2.4.16"
"superagent": "^1.2.0",
"superagent-bluebird-promise": "^2.0.2"
},
"devDependencies": {
"should": "^4.4.1",
"mocha": "^2.0.1"
"co-mocha": "^1.1.0",
"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");
var Router = require("react-router");
require("node-jsx").install({extension: ".jsx"});
var routes = require("../views/app.jsx").routes;
import React from 'react';
import createHandler from '../lib/react-handler';
import {routes} from '../views/app.jsx';
module.exports = function(req, res, next) {
req.db.matches.find().sort({"created_at": -1}).limit(6).toArray().then(function(docs){
var recents = [];
docs.forEach(function(doc) {
var shares = Object.keys(doc.services).map(function (key) {return doc.services[key]; });
shares.some(function(item) {
if (item.service === doc._id.split("$$")[0]) { // eslint-disable-line no-underscore-dangle
recents.push(item);
return false;
}
});
module.exports = function* () {
let recents = [];
let docs = yield this.db.matches.find().sort({'created_at': -1}).limit(6).toArray();
docs.forEach(function(doc) {
let shares = Object.keys(doc.services).map(function (key) {return doc.services[key]; });
shares.some(function(item) {
if (item.service === doc._id.split('$$')[0]) { // eslint-disable-line no-underscore-dangle
recents.push(item);
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";
var parse = require("url").parse;
var request = require("superagent");
import {parse} from 'url';
import request from 'superagent';
module.exports = function(req, res) {
var url = "http://" + req.url.substr(8);
var parsed = parse(url);
module.exports = function* (next) {
let url = 'http://' + this.request.url.substr(8);
let parsed = parse(url);
if (parsed.host.match(/mzstatic\.com/)) {
request.get(url, function(response){
res.set(response.headers);
res.send(response.body);
});
let proxyResponse = yield request.get(url);
this.set(proxyResponse.headers);
this.body = proxyResponse.body;
} else {
yield next;
}
};

View file

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

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");
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) {
let formatAndSort = function(matches, serviceId) {
matches = Object.keys(matches).map(function (key) {return matches[key]; });
matches.sort(function(a, b) {
return a.id && !b.id;
@ -17,60 +13,34 @@ var formatAndSort = function(matches, serviceId) {
return matches;
};
module.exports = function(req, res, next) {
var serviceId = req.params.service;
var type = req.params.type;
var itemId = req.params.id;
var matchedService;
module.exports = function* (serviceId, type, itemId, format, next) {
let matchedService;
services.some(function(service) {
matchedService = serviceId === service.id ? service : null;
return matchedService;
});
if (!matchedService || (type !== "album" && type !== "track")) {
return next();
if (!matchedService || (type !== 'album' && type !== 'track')) {
return yield next;
}
return req.db.matches.findOne({_id: serviceId + "$$" + itemId}).then(function(doc) {
if (!doc) {
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);
});
let shares = [];
let doc = yield this.db.matches.findOne({_id: serviceId + '$$' + itemId});
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";
require('should');
var lookup = require("../lib/lookup");
import 'should';
import lookup from '../lib/lookup';
describe('Search with url', function(){
it('should find album by url', function(done){
lookup("https://play.google.com/music/listen#/album/Bz6wrjczddcj5hurijsv6ohdoay").then(function(result) {
result.name.should.equal("Phase 5");
done();
});
it('should find album by url', function* (){
let result = yield lookup('https://play.google.com/music/listen#/album/Bz6wrjczddcj5hurijsv6ohdoay');
result.name.should.equal('Phase 5');
});
});
});

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,16 +1,11 @@
"use strict";
var assert = require("assert");
var should = require('should');
var youtube = require("../../lib/services/youtube");
import 'should';
import youtube from '../../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();
});
it('should find album by search', function* (){
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');
});
});
});

View file

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

View file

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

View file

@ -65,7 +65,7 @@ var SearchForm = React.createClass({
});
return;
}
request.post("/search").send({url: url}).end(function(res) {
request.post("/search").send({url: url}).end(function(req, res) {
that.setState({
submitting: false
});
@ -118,7 +118,7 @@ module.exports = React.createClass({
componentDidMount: function () {
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({
recents: res.body.recents
});

View file

@ -100,7 +100,7 @@ module.exports = React.createClass({
});
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;
complete = true;
shares.forEach(function(share) {