diff --git a/LICENSE b/LICENSE index 0c558be..fd1783c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ (The MIT License) -Copyright (c) 20014 Jonathan Cremin +Copyright (c) 2014 Jonathan Cremin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/Procfile b/Procfile index 063b78f..60498b1 100644 --- a/Procfile +++ b/Procfile @@ -1 +1 @@ -web: npm start +web: npm run build && npm start diff --git a/README.md b/README.md index 55ccccc..675d204 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ #match.audio [![Circle CI](https://circleci.com/gh/kudos/match.audio.svg?style=svg)](https://circleci.com/gh/kudos/match.audio) -Make sharing from music services better. We match links from Rdio, Spotify, Deezer, Beats Music, Google Music and iTunes and give you back a link with all of them. +Make sharing from music services better. We match links from Rdio, Spotify, Deezer, Google Music and iTunes and give you back a link with all of them. ## Supported Services @@ -32,4 +32,4 @@ So there's no surprises for contributors later, I plan on using referral tags wh ## Licence -The code is MIT licenced, the brand is not. This applies to the logo, name and magenta colour scheme. I'll probably pull the branding out of the app itself at some point to make that distinction more clear. \ No newline at end of file +The code is MIT licenced, the brand is not. This applies to the logo, name and magenta colour scheme. I'll probably pull the branding out of the app itself at some point to make that distinction more clear. diff --git a/app.js b/app.js index f766f1d..756b95c 100644 --- a/app.js +++ b/app.js @@ -1,4 +1,5 @@ import path from 'path'; +import zlib from 'zlib'; import koa from 'koa'; import route from 'koa-route'; import logger from 'koa-logger'; @@ -13,8 +14,7 @@ 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'; -import zlib from 'zlib'; +import { routes } from './views/app'; import createHandler from './lib/react-handler'; import debuglog from 'debug'; @@ -29,6 +29,7 @@ app.use(function* (next) { } catch (err) { if (!err.status) { debug('Error: %o', err); + throw err; } else if (err.status === 404) { let Handler = yield createHandler(routes, this.request.url); @@ -90,20 +91,10 @@ app.use(route.get('/recent', function* () { this.body = {recents: recents}; })); -module.exports = app; - if (!module.parent) { - if (process.env.LOCALHOST_KEY) { - require('spdy').createServer({ - key: process.env.LOCALHOST_KEY, - cert: process.env.LOCALHOST_CRT - }, app.callback()).listen(3000, function() { - debug('Koa SPDY server listening on port ' + (process.env.PORT || 3000)); - }); - } else { - app.listen(process.env.PORT || 3000, function() { - debug('Koa HTTP server listening on port ' + (process.env.PORT || 3000)); - }); - } - + app.listen(process.env.PORT || 3000, function() { + debug('Koa HTTP server listening on port ' + (process.env.PORT || 3000)); + }); } + +export default app; diff --git a/bin/www b/bin/www deleted file mode 100755 index 720b602..0000000 --- a/bin/www +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env node -"use strict"; -var debug = require("debug")("match.audio"); -var app = require("../app"); - -app.set("port", process.env.PORT || 3000); - -var server = app.listen(app.get("port"), function() { - debug("Express server listening on port " + server.address().port); -}); diff --git a/config/db.js b/config/db.js index c1b5403..2486bc1 100644 --- a/config/db.js +++ b/config/db.js @@ -1,15 +1,11 @@ -'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; +import mongodb from 'mongodb-promisified'; +const MongoClient = mongodb().MongoClient; +import debuglog from 'debug'; +const debug = debuglog('match.audio'); const uristring = process.env.MONGOLAB_URI || process.env.MONGOHQ_URL || 'mongodb://localhost:27017/match-audio'; -module.exports = function*() { +export default function*() { const client = yield MongoClient.connect(uristring); debug('Successfully connected to Mongodb'); client.matches = client.collection('matches'); diff --git a/lib/lookup.js b/lib/lookup.js index e298b84..d6f226c 100644 --- a/lib/lookup.js +++ b/lib/lookup.js @@ -4,25 +4,19 @@ import fs from 'fs'; var services = []; fs.readdirSync(path.join(__dirname, 'services')).forEach(function(file) { - var service = require(path.join(__dirname, 'services', file)); + const service = require(path.join(__dirname, 'services', file)); if (service.search) { services.push(service); } }); -module.exports = function(url) { - - var matchedService; - services.some(function(service) { - matchedService = service.match(url) ? service : null; - return matchedService; - }); - - if (matchedService) { - return matchedService.parseUrl(url).timeout(10000).then(function(result) { - return matchedService.lookupId(result.id, result.type).then(function(item) { - return item; - }); - }); +export default function* (url) { + let matchedService; + for (let service of services) { + matchedService = yield service.match(url); + if (matchedService) { + const result = yield service.parseUrl(url); + return yield service.lookupId(result.id, result.type); + } } }; diff --git a/lib/services.js b/lib/services.js index 4555747..790c9f3 100644 --- a/lib/services.js +++ b/lib/services.js @@ -1,11 +1,13 @@ import path from 'path'; import fs from 'fs'; -module.exports = []; +const services = []; fs.readdirSync(path.join(__dirname, 'services')).forEach(function(file) { var service = require(path.join(__dirname, 'services', file)); if (service.search) { - module.exports.push(service); + services.push(service); } }); + +export default services; diff --git a/lib/services/deezer/index.js b/lib/services/deezer/index.js index 8c3b894..d8173d4 100644 --- a/lib/services/deezer/index.js +++ b/lib/services/deezer/index.js @@ -1,14 +1,15 @@ -import {parse} from 'url'; +import { parse } from 'url'; import request from 'superagent'; import 'superagent-bluebird-promise'; +import { match as urlMatch } from './url'; -module.exports.id = 'deezer'; +export let id = 'deezer'; const apiRoot = 'https://api.deezer.com'; -module.exports.match = require('./url').match; +export const match = urlMatch; -module.exports.parseUrl = function(url) { +export function parseUrl(url) { let matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/); if (matches && matches[2]) { @@ -18,7 +19,7 @@ module.exports.parseUrl = function(url) { } }; -module.exports.lookupId = function* (id, type) { +export function* lookupId(id, type) { let path = '/' + type + '/' + id; let {body} = yield request.get(apiRoot + path).promise(); @@ -74,7 +75,7 @@ module.exports.lookupId = function* (id, type) { } }; -module.exports.search = function* (data) { +export function* search(data) { let cleanParam = function(str) { return str.replace(/[\:\?\&]+/, ''); }; diff --git a/lib/services/deezer/url.js b/lib/services/deezer/url.js index daea1b7..f479ac6 100644 --- a/lib/services/deezer/url.js +++ b/lib/services/deezer/url.js @@ -1,11 +1,10 @@ -"use strict"; -var parse = require('url').parse; +import { parse } from 'url'; -module.exports.match = function(url) { - var parsed = parse(url); +export function* match(url) { + const parsed = parse(url); if (!parsed.host.match(/deezer\.com$/)) { return false; } - var matches = parsed.path.match(/\/(album|track)[\/]+([^\/]+)/); + const matches = parsed.path.match(/\/(album|track)[\/]+([^\/]+)/); return matches.length > 1; }; diff --git a/lib/services/google/index.js b/lib/services/google/index.js index 97e9203..16be600 100644 --- a/lib/services/google/index.js +++ b/lib/services/google/index.js @@ -1,148 +1,135 @@ -"use strict"; +import { parse } from 'url'; +import bluebird from 'bluebird'; +import PlayMusic from 'playmusic'; +import { match as urlMatch } from './url'; -var parse = require("url").parse; -var Promise = require("bluebird"); -var PlayMusic = require("playmusic"); -var pm = Promise.promisifyAll(new PlayMusic()); +const pm = bluebird.promisifyAll(new PlayMusic()); -module.exports.id = "google"; +export let id = 'google'; if (!process.env.GOOGLE_EMAIL || !process.env.GOOGLE_PASSWORD) { - console.warn("GOOGLE_EMAIL or GOOGLE_PASSWORD environment variables not found, deactivating Google Play Music."); + console.warn('GOOGLE_EMAIL or GOOGLE_PASSWORD environment variables not found, deactivating Google Play Music.'); } -var ready = pm.initAsync({email: process.env.GOOGLE_EMAIL, password: process.env.GOOGLE_PASSWORD}).catch(function(err) { +let ready = pm.initAsync({email: process.env.GOOGLE_EMAIL, password: process.env.GOOGLE_PASSWORD}).catch(function(err) { console.log(err); }); -module.exports.match = require("./url").match; +export const match = urlMatch; -module.exports.parseUrl = function(url) { - return ready.then(function() { - var parsed = parse(url.replace(/\+/g, "%20")); - var path = parsed.path; - var hash = parsed.hash; - if (hash) { - var parts = hash.split("/"); - var type = parts[1]; - var id = parts[2]; - var artist = decodeURIComponent(parts[3]); - var album = decodeURIComponent(parts[4]); +export function* parseUrl(url) { + yield ready; + const parsed = parse(url.replace(/\+/g, '%20')); + const path = parsed.path; + const hash = parsed.hash; + if (hash) { + const parts = hash.split('/'); + const type = parts[1]; + const id = parts[2]; + const artist = decodeURIComponent(parts[3]); + const album = decodeURIComponent(parts[4]); - if (type !== "album" && type !== "track") { - return false; - } - - if (id.length > 0) { - return {id: id, type: type}; - } else { - return module.exports.search({type: type, name: album, artist: {name: artist}}); - } - } else if(path) { - var matches = path.match(/\/music\/m\/([\w]+)/); - type = matches[1][0] === "T" ? "track" : "album"; - return module.exports.lookupId(matches[1], type); - } - return false; - }); -}; - -module.exports.lookupId = function(id, type) { - return ready.then(function() { - if (type === "album") { - return pm.getAlbumAsync(id, false).then(function(album) { - return { - service: "google", - type: "album", - id: album.albumId, - name: album.name, - streamUrl: "https://play.google.com/music/m/" + album.albumId + "?signup_if_needed=1", - purchaseUrl: "https://play.google.com/store/music/album?id=" + album.albumId, - artwork: { - small: album.albumArtRef.replace("http:", "https:"), - large: album.albumArtRef.replace("http:", "https:") - }, - artist: { - name: album.artist - } - }; - }, function(error) { - throw error; - }); - } else if (type === "track") { - return pm.getAllAccessTrackAsync(id).then(function(track) { - return { - service: "google", - type: "track", - id: track.nid, - name: track.title, - streamUrl: "https://play.google.com/music/m/" + track.nid + "?signup_if_needed=1", - purchaseUrl: "https://play.google.com/store/music/album?id=" + track.albumId, - artwork: { - small: track.albumArtRef[0].url.replace("http:", "https:"), - large: track.albumArtRef[0].url.replace("http:", "https:") - }, - album: { - name: track.album - }, - artist: { - name: track.artist - } - }; - }, function(error) { - throw error; - }); - } - }); -}; - -module.exports.search = function(data) { - return ready.then(function() { - var query, album; - var type = data.type; - - if (type === "album") { - query = data.artist.name + " " + data.name; - album = data.name; - } else if (type === "track") { - query = data.artist.name + " " + data.album.name + " " + data.name; - album = data.album.name; + if (type !== 'album' && type !== 'track') { + return false; } - return pm.searchAsync(query, 5).then(function(result) { - - if (!result.entries) { - 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: "google"}; - } - } - result = result.entries.filter(function(entry) { - return entry[type]; - }).sort(function(a, b) { // sort by match score - return a.score < b.score; - }).shift(); - - if (!result) { - return {service: "google"}; - } else { - var id; - if (type === "album") { - id = result.album.albumId; - } else if (type === "track") { - id = result.track.nid; - } - - return module.exports.lookupId(id, type); - } - }); - }); + if (id.length > 0) { + return {id: id, type: type}; + } else { + return yield search({type: type, name: album, artist: {name: artist}}); + } + } else if(path) { + const matches = path.match(/\/music\/m\/([\w]+)/); + const type = matches[1][0] === 'T' ? 'track' : 'album'; + return yield lookupId(matches[1], type); + } + return false; +}; + +export function* lookupId(id, type) { + yield ready; + if (type === 'album') { + const album = yield pm.getAlbumAsync(id, false); + return { + service: 'google', + type: 'album', + id: album.albumId, + name: album.name, + streamUrl: 'https://play.google.com/music/m/' + album.albumId + '?signup_if_needed=1', + purchaseUrl: 'https://play.google.com/store/music/album?id=' + album.albumId, + artwork: { + small: album.albumArtRef.replace('http:', 'https:'), + large: album.albumArtRef.replace('http:', 'https:') + }, + artist: { + name: album.artist + } + }; + } else if (type === 'track') { + const track = yield pm.getAllAccessTrackAsync(id); + return { + service: 'google', + type: 'track', + id: track.nid, + name: track.title, + streamUrl: 'https://play.google.com/music/m/' + track.nid + '?signup_if_needed=1', + purchaseUrl: 'https://play.google.com/store/music/album?id=' + track.albumId, + artwork: { + small: track.albumArtRef[0].url.replace('http:', 'https:'), + large: track.albumArtRef[0].url.replace('http:', 'https:') + }, + album: { + name: track.album + }, + artist: { + name: track.artist + } + }; + } +}; + +export function* search(data) { + yield ready; + let query, album; + const type = data.type; + + if (type === 'album') { + query = data.artist.name + ' ' + data.name; + album = data.name; + } else if (type === 'track') { + query = data.artist.name + ' ' + data.album.name + ' ' + data.name; + album = data.album.name; + } + + let result = yield pm.searchAsync(query, 5) + + if (!result.entries) { + const matches = album.match(/^[^\(\[]+/); + if (matches && matches[0] && matches[0] !== album) { + const 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 search(cleanedData); + } else { + return {service: 'google'}; + } + } + result = result.entries.filter(function(entry) { + return entry[type]; + }).sort(function(a, b) { // sort by match score + return a.score < b.score; + }).shift(); + + if (!result) { + return {service: 'google'}; + } else { + if (type === 'album') { + return yield lookupId(result.album.albumId, type); + } else if (type === 'track') { + return yield lookupId(result.track.nid, type); + } + } }; diff --git a/lib/services/google/url.js b/lib/services/google/url.js index 8fac38e..e4066c7 100644 --- a/lib/services/google/url.js +++ b/lib/services/google/url.js @@ -1,19 +1,18 @@ -"use strict"; -var parse = require('url').parse; +import { parse } from 'url'; -module.exports.match = function(url) { +export function* match(url) { var parsed = parse(url.replace(/\+/g, "%20")); if (!parsed.host.match(/play\.google\.com$/)) { return false; } - var path = parsed.path; - var hash = parsed.hash; + const path = parsed.path; + const hash = parsed.hash; if (hash) { - var parts = hash.split("/"); - var id = parts[2]; - var artist = parts[3]; + const parts = hash.split("/"); + const id = parts[2]; + const artist = parts[3]; if (id.length > 0) { return true; @@ -21,7 +20,7 @@ module.exports.match = function(url) { return true; } } else if(path) { - var matches = path.match(/\/music\/m\/([\w]+)/); + const matches = path.match(/\/music\/m\/([\w]+)/); if (matches[1]) { return true } diff --git a/lib/services/itunes/index.js b/lib/services/itunes/index.js index 6801930..9de52c0 100644 --- a/lib/services/itunes/index.js +++ b/lib/services/itunes/index.js @@ -1,18 +1,19 @@ -import {parse} from 'url'; +import { parse } from 'url'; import querystring from 'querystring'; import request from 'superagent'; import 'superagent-bluebird-promise'; +import { match as urlMatch } from './url'; -module.exports.id = 'itunes'; +export let id = 'itunes'; const apiRoot = 'https://itunes.apple.com'; -module.exports.match = require('./url').match; +export const match = urlMatch; -module.exports.parseUrl = function* (url) { - let parsed = parse(url); - let matches = parsed.path.match(/[\/]?([\/]?[a-z]{2}?)?[\/]+album[\/]+([^\/]+)[\/]+([^\?]+)/); - let query = querystring.parse(parsed.query); +export function* parseUrl(url) { + const parsed = parse(url); + const matches = parsed.path.match(/[\/]?([\/]?[a-z]{2}?)?[\/]+album[\/]+([^\/]+)[\/]+([^\?]+)/); + const query = querystring.parse(parsed.query); if (matches) { let type = 'album'; @@ -23,11 +24,11 @@ module.exports.parseUrl = function* (url) { } return yield module.exports.lookupId(id, type, matches[1] || 'us'); } else { - return Promise.reject(new Error()); + throw new Error(); } }; -module.exports.lookupId = function* (id, type, cc) { +export function* lookupId(id, type, cc) { if (String(id).match(/^[a-z]{2}/)) { cc = id.substr(0, 2); id = id.substr(2); @@ -38,13 +39,13 @@ module.exports.lookupId = function* (id, type, cc) { path = '/' + cc + path; } - let response = yield request.get(apiRoot + path); + const response = yield request.get(apiRoot + path); let result = JSON.parse(response.text); if (!result.results || result.resultCount === 0 || !result.results[0].collectionId) { - let error = new Error('Not Found'); + const error = new Error('Not Found'); error.status = 404; - return Promise.reject(error); + throw error; } else { result = result.results[0]; @@ -70,13 +71,13 @@ module.exports.lookupId = function* (id, type, cc) { }; } - return Promise.resolve(item); + return item; } }; -module.exports.search = function* (data) { +export function* search(data) { let query, album, entity; - let type = data.type; + const type = data.type; if (type === 'album') { query = data.artist.name + ' ' + data.name; @@ -88,27 +89,27 @@ module.exports.search = function* (data) { entity = 'musicTrack'; } - let path = '/search?term=' + encodeURIComponent(query) + '&media=music&entity=' + entity; - let response = yield request.get(apiRoot + path); + const path = '/search?term=' + encodeURIComponent(query) + '&media=music&entity=' + entity; + const response = yield request.get(apiRoot + path); let result = JSON.parse(response.text); if (!result.results[0]) { - let matches = album.match(/^[^\(\[]+/); + const matches = album.match(/^[^\(\[]+/); if (matches && matches[0] && matches[0] !== album) { - let cleanedData = JSON.parse(JSON.stringify(data)); + const 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); + return yield search(cleanedData); } else { - return Promise.resolve({service: 'itunes'}); + return {service: 'itunes'}; } } else { result = result.results[0]; - let item = { + const item = { service: 'itunes', type: type, id: 'us' + result.collectionId, @@ -129,6 +130,6 @@ module.exports.search = function* (data) { name: result.collectionName }; } - return Promise.resolve(item); + return item; } }; diff --git a/lib/services/itunes/url.js b/lib/services/itunes/url.js index eaf86c2..22b3ed6 100644 --- a/lib/services/itunes/url.js +++ b/lib/services/itunes/url.js @@ -1,16 +1,15 @@ -"use strict"; -var parse = require('url').parse; -var querystring = require('querystring'); +import { parse } from 'url'; +import querystring from 'querystring'; -module.exports.match = function(url, type) { - var parsed = parse(url); +export function* match(url, type) { + const parsed = parse(url); if (!parsed.host.match(/itunes.apple\.com$/)) { return false; } - var matches = parsed.path.match(/[\/]?([\/]?[a-z]{2}?)?[\/]+album[\/]+([^\/]+)[\/]+([^\?]+)/); - var query = querystring.parse(parsed.query); + const matches = parsed.path.match(/[\/]?([\/]?[a-z]{2}?)?[\/]+album[\/]+([^\/]+)[\/]+([^\?]+)/); + const query = querystring.parse(parsed.query); return !!matches[3]; }; diff --git a/lib/services/rdio/url.js b/lib/services/rdio/url.js index 7e8c530..8fb7f40 100644 --- a/lib/services/rdio/url.js +++ b/lib/services/rdio/url.js @@ -1,11 +1,11 @@ import { parse } from 'url'; -export function match(url) { - var parsed = parse(url); +export function* match(url) { + const parsed = parse(url); if (!parsed.host.match(/rd\.io$/) && !parsed.host.match(/rdio\.com$/)) { return false; } - var regular = parsed.path.match(/[\/]*artist[\/]*([^\/]*)[\/]*album[\/]*([^\/]*)[\/]*([track]*)?[\/]*([^\/]*)/); - var short = parsed.path.match(/[\/]*x[\/]*([^\/]*)/); + const regular = parsed.path.match(/[\/]*artist[\/]*([^\/]*)[\/]*album[\/]*([^\/]*)[\/]*([track]*)?[\/]*([^\/]*)/); + const short = parsed.path.match(/[\/]*x[\/]*([^\/]*)/); return (regular && !!regular[2]) || (short && !!short[1]); }; diff --git a/lib/services/spotify/index.js b/lib/services/spotify/index.js index 3e3b855..da15d18 100644 --- a/lib/services/spotify/index.js +++ b/lib/services/spotify/index.js @@ -1,70 +1,70 @@ -"use strict"; -var parse = require('url').parse; -var Promise = require('bluebird'); -var spotify = Promise.promisifyAll(require('spotify')); +import { parse } from 'url'; +import bluebird from 'bluebird'; +import spotifyCB from 'spotify'; +const spotify = bluebird.promisifyAll(spotifyCB); +import { match as urlMatch } from './url'; -module.exports.id = "spotify"; +export let id = "spotify"; -module.exports.match = require('./url').match; +export const match = urlMatch; -module.exports.parseUrl = function(url) { +export function* parseUrl(url) { var matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/); if (matches && matches[2]) { - return module.exports.lookupId(matches[2], matches[1]); + return yield lookupId(matches[2], matches[1]); } } -module.exports.lookupId = function(id, type) { - return spotify.lookupAsync({id: id, type: type}).then(function(data) { - if (data.error) { - var error = new Error("Not Found"); - error.status = 404; - throw error; - } +export function* lookupId(id, type) { + const data = yield spotify.lookupAsync({id: id, type: type}); + if (data.error) { + var error = new Error("Not Found"); + error.status = 404; + throw error; + } - var artist = data.artists[0]; + var artist = data.artists[0]; - if (type == "album") { - return { - service: "spotify", - type: type, - id: data.id, - name: data.name, - streamUrl: "https://play.spotify.com/" + type + "/" + data.id, - purchaseUrl: null, - artwork: { - small: data.images[1].url.replace("http:", "https:"), - large: data.images[0].url.replace("http:", "https:"), - }, - artist: { - name: artist.name - } - }; - } else if (type == "track") { - return { - service: "spotify", - type: type, - id: data.id, - name: data.name, - streamUrl: "https://play.spotify.com/" + type + "/" + data.id, - purchaseUrl: null, - artwork: { - small: data.album.images[1].url.replace("http:", "https:"), - large: data.album.images[0].url.replace("http:", "https:"), - }, - artist: { - name: artist.name - }, - album: { - name: data.album.name - } - }; - } - }); + if (type == "album") { + return { + service: "spotify", + type: type, + id: data.id, + name: data.name, + streamUrl: "https://play.spotify.com/" + type + "/" + data.id, + purchaseUrl: null, + artwork: { + small: data.images[1].url.replace("http:", "https:"), + large: data.images[0].url.replace("http:", "https:"), + }, + artist: { + name: artist.name + } + }; + } else if (type == "track") { + return { + service: "spotify", + type: type, + id: data.id, + name: data.name, + streamUrl: "https://play.spotify.com/" + type + "/" + data.id, + purchaseUrl: null, + artwork: { + small: data.album.images[1].url.replace("http:", "https:"), + large: data.album.images[0].url.replace("http:", "https:"), + }, + artist: { + name: artist.name + }, + album: { + name: data.album.name + } + }; + } } -module.exports.search = function(data) { +export function* search(data) { var cleanParam = function(str) { var chopChars = ['&', '[', '(']; chopChars.forEach(function(chr) { @@ -85,28 +85,26 @@ module.exports.search = function(data) { album = data.album.name; } - return spotify.searchAsync({query: query, type: type}).then(function(results) { - if (!results[type + "s"].items[0]) { - return {service: "spotify"}; - } else { - var found; - var choppedAlbum = data.type == "album" ? cleanParam(data.name) : cleanParam(data.album.name); - if (!choppedAlbum.length) { - return module.exports.lookupId(results[type + "s"].items[0].id, type); - } - - results[type + "s"].items.forEach(function(item) { - var albumName = data.type == "album" ? item.name : item.album.name; - var matches = albumName.match(/^[^\(\[]+/); - if(choppedAlbum.indexOf(matches[0]) >= 0) { - found = item; - } - }); - if (!found) { - return {service: "spotify"}; - } - return module.exports.lookupId(results[type + "s"].items[0].id, type); + const results = yield spotify.searchAsync({query: query, type: type}); + if (!results[type + "s"].items[0]) { + return {service: "spotify"}; + } else { + let found; + const choppedAlbum = data.type == "album" ? cleanParam(data.name) : cleanParam(data.album.name); + if (!choppedAlbum.length) { + return yield lookupId(results[type + "s"].items[0].id, type); } - }); + results[type + "s"].items.forEach(function(item) { + const albumName = data.type == "album" ? item.name : item.album.name; + const matches = albumName.match(/^[^\(\[]+/); + if(choppedAlbum.indexOf(matches[0]) >= 0) { + found = item; + } + }); + if (!found) { + return {service: "spotify"}; + } + return yield lookupId(results[type + "s"].items[0].id, type); + } } diff --git a/lib/services/spotify/url.js b/lib/services/spotify/url.js index 46a80d5..0ae7c77 100644 --- a/lib/services/spotify/url.js +++ b/lib/services/spotify/url.js @@ -1,12 +1,11 @@ -"use strict"; -var parse = require('url').parse; +import { parse } from 'url'; -module.exports.match = function(url, type) { - var parsed = parse(url); +export function* match(url, type) { + const parsed = parse(url); if (!parsed.host.match(/spotify\.com$/)) { return false; } - var matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/); + const matches = parse(url).path.match(/\/(album|track)[\/]+([^\/]+)/); return matches && !!matches[2]; }; diff --git a/lib/services/xbox/index.js b/lib/services/xbox/index.js index 0f5b791..f95886b 100644 --- a/lib/services/xbox/index.js +++ b/lib/services/xbox/index.js @@ -1,40 +1,45 @@ -"use strict"; -var parse = require('url').parse; -var Promise = require('bluebird'); -var request = require('superagent'); -require('superagent-bluebird-promise'); +import { parse } from 'url'; +import querystring from 'querystring'; +import request from 'superagent'; +import 'superagent-bluebird-promise'; +import { match as urlMatch } from './url'; -module.exports.id = "xbox"; +export let 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."); -} else { +} -var credentials = { +const credentials = { clientId: process.env.XBOX_CLIENT_ID, clientSecret: process.env.XBOX_CLIENT_SECRET }; -var apiRoot = "https://music.xboxlive.com/1/content"; +const apiRoot = "https://music.xboxlive.com/1/content"; -var getAccessToken = function() { - var authUrl = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"; - var scope = "http://music.xboxlive.com"; - var grantType = "client_credentials"; - - var data = {client_id: credentials.clientId, client_secret: credentials.clientSecret, scope: scope, grant_type: grantType}; - return request.post(authUrl).send(data).set('Content-type', 'application/x-www-form-urlencoded').promise().then(function(res) { - return res.body.access_token; - }); +function* getAccessToken() { + const authUrl = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13"; + const scope = "http://music.xboxlive.com"; + const grantType = "client_credentials"; + + const data = { + client_id: credentials.clientId, + client_secret: credentials.clientSecret, + scope: scope, + grant_type: grantType + }; + const result = yield request.post(authUrl).send(data).set('Content-type', 'application/x-www-form-urlencoded').promise(); + return result.body.access_token; } -var formatResponse = function(res) { +function formatResponse(res) { + let result; if (res.body.Tracks) { - var result = res.body.Tracks.Items[0]; + result = res.body.Tracks.Items[0]; } else { - var result = res.body.Albums.Items[0]; + result = res.body.Albums.Items[0]; } - var item = { + let item = { service: "xbox", type: res.body.Tracks ? "track" : "album", id: result.Id, @@ -55,37 +60,33 @@ var formatResponse = function(res) { return item; } -module.exports.match = require('./url').match; +export const match = urlMatch; -module.exports.parseUrl = function(url) { - var parsed = parse(url); - var parts = parsed.path.split("/"); - var type = parts[1]; - var idMatches = parts[4].match(/[\w\-]+/); - var id = idMatches[0]; +export function* parseUrl(url) { + const parsed = parse(url); + const parts = parsed.path.split("/"); + const type = parts[1]; + const idMatches = parts[4].match(/[\w\-]+/); + const id = idMatches[0]; if (!id) { return false; } - return module.exports.lookupId("music." + id, type); + return yield lookupId("music." + id, type); } -module.exports.lookupId = function(id, type) { - return getAccessToken().then(function(access_token){ - var path = "/" + id + "/lookup"; - return request.get(apiRoot + path).set("Authorization", "Bearer " + access_token).promise().then(function(res) { - return formatResponse(res); - }, function(res) { - return {service: "xbox"}; - }); - }); +export function* lookupId(id, type) { + const access_token = yield getAccessToken(); + const path = "/" + id + "/lookup"; + const result = yield request.get(apiRoot + path).set("Authorization", "Bearer " + access_token).promise(); + return result ? formatResponse(result) : {service: "xbox"}; }; -module.exports.search = function(data) { +export function* search(data) { var cleanParam = function(str) { return str.replace(/[\:\?\&]+/, ""); } - var query, album; - var type = data.type; + let query, album; + const type = data.type; if (type == "album") { query = cleanParam(data.artist.name.substring(0, data.artist.name.indexOf('&'))) + " " + cleanParam(data.name); @@ -94,14 +95,8 @@ module.exports.search = function(data) { query = cleanParam(data.artist.name.substring(0, data.artist.name.indexOf('&'))) + " " + cleanParam(data.name); album = data.album.name } - return getAccessToken().then(function(access_token){ - var path = "/music/search?q=" + encodeURIComponent(query) + "&filters=" + type + "s"; - return request.get(apiRoot + path).set("Authorization", "Bearer " + access_token).promise().then(function(res) { - return formatResponse(res); - }, function(res) { - return {service: "xbox"}; - }); - }); + const access_token = yield getAccessToken(); + const path = "/music/search?q=" + encodeURIComponent(query) + "&filters=" + type + "s"; + const result = yield request.get(apiRoot + path).set("Authorization", "Bearer " + access_token).promise() + return result ? formatResponse(result) : {service: "xbox"}; }; - -} diff --git a/lib/services/xbox/url.js b/lib/services/xbox/url.js index 7d86290..d116d5d 100644 --- a/lib/services/xbox/url.js +++ b/lib/services/xbox/url.js @@ -1,14 +1,11 @@ -"use strict"; -var parse = require('url').parse; - -module.exports.match = function(url, type) { - var parsed = parse(url); +import { parse } from 'url'; +export function* match(url, type) { + const parsed = parse(url); if (!parsed.host.match(/music.xbox.com$/)) { return false; } - var parts = parsed.path.split("/"); - + const parts = parsed.path.split("/"); return (parts[1] == "album" || parts[1] == "track") && parts[4]; }; diff --git a/lib/services/youtube/freebase.js b/lib/services/youtube/freebase.js index fffc02e..2dd7793 100644 --- a/lib/services/youtube/freebase.js +++ b/lib/services/youtube/freebase.js @@ -1,20 +1,15 @@ -"use strict"; -var parse = require('url').parse; -var Promise = require('bluebird'); -var request = require('superagent'); -require('superagent-bluebird-promise'); +import { parse } from 'url'; +import querystring from 'querystring'; +import request from 'superagent'; +import 'superagent-bluebird-promise'; -var credentials = { +const credentials = { key: process.env.YOUTUBE_KEY, }; -var apiRoot = "https://www.googleapis.com/freebase/v1/topic"; +const apiRoot = "https://www.googleapis.com/freebase/v1/topic"; -module.exports.get = function(topic) { - return request.get(apiRoot + topic + "?key=" + credentials.key).promise().then(function(res) { - return res.body; - }) +export function* get(topic) { + const result = yield request.get(apiRoot + topic + "?key=" + credentials.key).promise(); + return result.body; } - - -module.exports.get("/m/0dwcrm_"); \ No newline at end of file diff --git a/lib/services/youtube/index.js b/lib/services/youtube/index.js index 9141ca3..1446b09 100644 --- a/lib/services/youtube/index.js +++ b/lib/services/youtube/index.js @@ -1,139 +1,124 @@ -"use strict"; -var parse = require('url').parse; -var freebase = require('./freebase'); -var querystring = require('querystring'); -var moment = require('moment'); -var Promise = require('bluebird'); -var request = require('superagent'); -require('superagent-bluebird-promise'); +import { parse } from 'url'; +import querystring from 'querystring'; +import moment from 'moment'; +import request from 'superagent'; +import 'superagent-bluebird-promise'; +import { match as urlMatch } from './url'; +import freebase from './freebase'; -module.exports.id = "youtube"; +module.exports.id = 'youtube'; if (!process.env.YOUTUBE_KEY) { - console.warn("YOUTUBE_KEY environment variable not found, deactivating Youtube."); -} else { + console.warn('YOUTUBE_KEY environment variable not found, deactivating Youtube.'); +} -var credentials = { +const credentials = { key: process.env.YOUTUBE_KEY, }; -var apiRoot = "https://www.googleapis.com/youtube/v3"; +const apiRoot = 'https://www.googleapis.com/youtube/v3'; -module.exports.match = require('./url').match; +export const match = urlMatch; + +export function parseUrl(url) { + const parsed = parse(url); + const query = querystring.parse(parsed.query); + let id = query.v; -module.exports.parseUrl = function(url) { - var parsed = parse(url); - var query = querystring.parse(parsed.query); - var id = query.v; - if (!id) { id = parsed.path.substr(1); if (!id) { throw new Error(); } } - return module.exports.lookupId(id, "track"); + return lookupId(id, 'track'); } -module.exports.lookupId = function(id, type) { - - var path = "/videos?part=snippet%2CtopicDetails%2CcontentDetails&id=" + id + "&key=" + credentials.key; - - return request.get(apiRoot + path).promise().then(function(res) { - var item = res.body.items[0]; - if (item.topicDetails.topicIds) { - var promises = []; - var match = { - id: id, - service: "youtube", - name: item.snippet.title, - type: "track", - album: {name: ""}, - streamUrl: "https://youtu.be/" + id, - purchaseUrl: null, - artwork: { - small: item.snippet.thumbnails.medium.url, - large: item.snippet.thumbnails.high.url, - } - }; - item.topicDetails.topicIds.forEach(function(topicId) { - promises.push(freebase.get(topicId).then(function(topic) { - if (topic.property["/type/object/type"].values.some(function(value) { - return value.text == "Musical Artist"; - })) { - match.artist = {name: topic.property["/type/object/name"].values[0].text}; - } else if (topic.property["/type/object/type"].values.some(function(value) { - return value.text == "Musical Recording"; - })) { - //if (moment.duration(item.contentDetails.duration).asSeconds() < 900) { - match.name = topic.property["/type/object/name"].values[0].text; - if (topic.property["/music/recording/releases"]) { - match.type = "album"; - match.album.name = topic.property["/music/recording/releases"].values[0].text; - } - //} - } else if (topic.property["/type/object/type"].values.some(function(value) { - return value.text == "Musical Album"; - })) { - match.name = topic.property["/type/object/name"].values[0].text; - match.type = "album"; - } - }, function(err) { - console.log(err) - })); - }); - return Promise.all(promises).then(function() { - return match; - }, function(err) { - console.log(err) - return {service: "youtube"}; - }); - } else { - return {service: "youtube"}; +export function* lookupId(id, type) { + + const path = '/videos?part=snippet%2CtopicDetails%2CcontentDetails&id=' + id + '&key=' + credentials.key; + + const result = yield request.get(apiRoot + path).promise(); + const item = res.body.items[0]; + if (!item.topicDetails.topicIds) { + return {service: 'youtube'}; + } + + const promises = []; + const match = { + id: id, + service: 'youtube', + name: item.snippet.title, + type: 'track', + album: {name: ''}, + streamUrl: 'https://youtu.be/' + id, + purchaseUrl: null, + artwork: { + small: item.snippet.thumbnails.medium.url, + large: item.snippet.thumbnails.high.url, } - }, function(err) { - console.log(err) - return {service: "youtube"}; - }); + }; + + for (let topic of yield freebase.get(topicId)) { + const musicalArtist = topic.property['/type/object/type'].values.some((value) => { + return value.text == 'Musical Artist'; + }); + + const musicalRecording = topic.property['/type/object/type'].values.some(function(value) { + return value.text == 'Musical Recording'; + }); + + const musicalAlbum = topic.property['/type/object/type'].values.some(function(value) { + return value.text == 'Musical Album'; + }) + + if (musicalArtist) { + match.artist = {name: topic.property['/type/object/name'].values[0].text}; + } else if (musicalRecording) { + match.name = topic.property['/type/object/name'].values[0].text; + if (topic.property['/music/recording/releases']) { + match.type = 'album'; + match.album.name = topic.property['/music/recording/releases'].values[0].text; + } + } else if (musicalAlbum) { + match.name = topic.property['/type/object/name'].values[0].text; + match.type = 'album'; + } + } + return match; }; -module.exports.search = function(data) { - var query, album; - var type = data.type; +export function* search(data) { + let query, album; + const type = data.type; - if (type == "album") { - query = data.artist.name + " " + data.name; + if (type == 'album') { + query = data.artist.name + ' ' + data.name; album = data.name; - } else if (type == "track") { - query = data.artist.name + " " + data.name; + } else if (type == 'track') { + query = data.artist.name + ' ' + data.name; album = data.album.name } - var path = "/search?part=snippet&q=" + encodeURIComponent(query) + "&type=video&videoCaption=any&videoCategoryId=10&key=" + credentials.key; + const path = '/search?part=snippet&q=' + encodeURIComponent(query) + '&type=video&videoCaption=any&videoCategoryId=10&key=' + credentials.key; - return request.get(apiRoot + path).promise().then(function(res) { - var result = res.body.items[0]; + const result = yield request.get(apiRoot + path).promise(); + const item = result.body.items[0]; - if (!result) { - return {service:"youtube", type: "video"}; - } else { - return { - service: "youtube", - type: "video", - id: result.id.videoId, - name: result.snippet.title, - streamUrl: "https://www.youtube.com/watch?v=" + result.id.videoId, - purchaseUrl: null, - artwork: { - small: result.snippet.thumbnails.medium.url, - large: result.snippet.thumbnails.high.url, - } - }; - } - }, function(err) { - console.log(err) - return {service: "youtube"}; - }); + if (!item) { + return {service:'youtube', type: 'video'}; + } else { + return { + service: 'youtube', + type: 'video', + id: item.id.videoId, + name: item.snippet.title, + streamUrl: 'https://www.youtube.com/watch?v=' + item.id.videoId, + purchaseUrl: null, + artwork: { + small: item.snippet.thumbnails.medium.url, + large: item.snippet.thumbnails.high.url, + } + }; + } }; - -} diff --git a/lib/services/youtube/url.js b/lib/services/youtube/url.js index 14fc82f..3ea5236 100644 --- a/lib/services/youtube/url.js +++ b/lib/services/youtube/url.js @@ -1,14 +1,12 @@ -"use strict"; -var parse = require("url").parse; -var querystring = require('querystring'); +import { parse } from 'url'; +import querystring from 'querystring'; -module.exports.match = function(url, type) { - var parsed = parse(url); - +export function* match(url, type) { + const parsed = parse(url); if (parsed.host.match(/youtu\.be$/)) { return true; } else if (parsed.host.match(/youtube\.com$/)) { - var query = querystring.parse(parsed.query); + const query = querystring.parse(parsed.query); return !!query.v; } return false; diff --git a/package.json b/package.json index a9e8229..5609350 100644 --- a/package.json +++ b/package.json @@ -1,26 +1,24 @@ { "name": "match.audio", "version": "0.0.0", - "private": true, + "repository": "https://github.com/kudos/match.audio", + "license": "MIT", "scripts": { "build": "babel -d public/views views", "cover": "istanbul cover _mocha -- --require babel/register --require co-mocha test/**/*.js", "start": "babel-node app.js", - "postinstall": "npm run build", "test": "mocha --require co-mocha --compilers js:babel/register test/**/*.js --timeout=10000", "watch": "parallelshell \"npm run watch-js\" \"npm run watch-server\"", "watch-js": "babel --modules system -wd public/views views", "watch-server": "nodemon -x \"babel-node\" -e js,jsx -i public/ app.js" }, "engines": { - "iojs": "^2.5.0", + "iojs": "^3.1.0", "npm": "^3.3.0" }, "dependencies": { "babel": "~5.8.3", - "babelify": "~6.1.3", "bluebird": "~2.9.34", - "browserify": "~11.0.0", "co": "~4.6.0", "debug": "~2.2.0", "jspm": "~0.16.0", @@ -39,7 +37,6 @@ "react": "~0.13.3", "react-google-analytics": "~0.2.0", "react-router": "~0.13.3", - "reactify": "~1.1.1", "spotify": "~0.3.0", "superagent": "~1.3.0", "superagent-bluebird-promise": "~2.0.2" @@ -52,8 +49,7 @@ "mocha": "~2.2.5", "nodemon": "~1.4.1", "parallelshell": "~2.0.0", - "should": "~7.0.1", - "spdy": "~2.0.4" + "should": "~7.0.1" }, "jspm": { "directories": { diff --git a/routes/index.js b/routes/index.js index 75df717..6323a97 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,12 +1,10 @@ -'use strict'; - import React from 'react'; import createHandler from '../lib/react-handler'; -import {routes} from '../views/app'; +import { routes } from '../views/app'; -module.exports = function* () { - let recents = []; - let docs = yield this.db.matches.find().sort({'created_at': -1}).limit(6).toArray(); +export default function* () { + const recents = []; + const 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) { @@ -17,9 +15,9 @@ module.exports = function* () { }); }); - let Handler = yield createHandler(routes, this.request.url); + const Handler = yield createHandler(routes, this.request.url); - let App = React.createFactory(Handler); + const App = React.createFactory(Handler); let content = React.renderToString(new App({recents: recents})); content = content.replace('', ''); diff --git a/routes/itunes-proxy.js b/routes/itunes-proxy.js index b96cc93..67ab7b6 100644 --- a/routes/itunes-proxy.js +++ b/routes/itunes-proxy.js @@ -1,11 +1,11 @@ -import {parse} from 'url'; +import { parse } from 'url'; import request from 'superagent'; -module.exports = function* (next) { - let url = 'http://' + this.request.url.substr(8); - let parsed = parse(url); +export default function* (next) { + const url = 'http://' + this.request.url.substr(8); + const parsed = parse(url); if (parsed.host.match(/mzstatic\.com/)) { - let proxyResponse = yield request.get(url); + const proxyResponse = yield request.get(url); this.set(proxyResponse.headers); this.body = proxyResponse.body; } else { diff --git a/routes/search.js b/routes/search.js index b336d20..7092e59 100644 --- a/routes/search.js +++ b/routes/search.js @@ -1,18 +1,18 @@ -import {parse} from 'url'; +import { parse } from 'url'; import co from 'co'; import lookup from '../lib/lookup'; import services from '../lib/services'; module.exports = function* () { - let url = parse(this.request.body.url); + const url = parse(this.request.body.url); this.assert(url.host, 400, {error: {message: 'You need to submit a url.'}}); - let item = yield lookup(this.request.body.url); + const item = yield lookup(this.request.body.url); this.assert(item, 400, {error: {message: 'No supported music found at that link :('}}); item.matched_at = new Date(); // eslint-disable-line camelcase - let matches = {}; + const matches = {}; matches[item.service] = item; @@ -32,9 +32,9 @@ module.exports = function* () { continue; } matches[service.id] = {service: service.id}; - let match = yield service.search(item); + const match = yield service.search(item); match.matched_at = new Date(); // eslint-disable-line camelcase - let update = {}; + const update = {}; update['services.' + match.service] = match; yield this.db.matches.updateOne({_id: item.service + '$$' + item.id}, {'$set': update}); } diff --git a/routes/share.js b/routes/share.js index 8432779..1999072 100644 --- a/routes/share.js +++ b/routes/share.js @@ -1,9 +1,9 @@ import React from 'react'; import createHandler from '../lib/react-handler'; -import {routes} from '../views/app'; +import { routes } from '../views/app'; import services from '../lib/services'; -let formatAndSort = function(matches, serviceId) { +function formatAndSort(matches, serviceId) { matches = Object.keys(matches).map(function (key) {return matches[key]; }); matches.sort(function(a, b) { return a.id && !b.id; @@ -13,7 +13,7 @@ let formatAndSort = function(matches, serviceId) { return matches; }; -module.exports = function* (serviceId, type, itemId, format, next) { +export default function* (serviceId, type, itemId, format, next) { let matchedService; services.some(function(service) { matchedService = serviceId === service.id ? service : null; @@ -24,19 +24,18 @@ module.exports = function* (serviceId, type, itemId, format, next) { return yield next; } - let shares = []; - let doc = yield this.db.matches.findOne({_id: serviceId + '$$' + itemId}); + const doc = yield this.db.matches.findOne({_id: serviceId + '$$' + itemId}); this.assert(doc, 404, 'Not Found'); - shares = formatAndSort(doc.services, serviceId); + const shares = formatAndSort(doc.services, serviceId); if (format === 'json') { this.body = {shares: shares}; } else { - let Handler = yield createHandler(routes, this.request.url); + const Handler = yield createHandler(routes, this.request.url); - let App = React.createFactory(Handler); + const App = React.createFactory(Handler); let content = React.renderToString(new App({shares: shares})); content = content.replace('', ''); diff --git a/test/lookup.js b/test/lookup.js index 27c7c5b..d4ae7fa 100644 --- a/test/lookup.js +++ b/test/lookup.js @@ -3,7 +3,7 @@ import lookup from '../lib/lookup'; describe('Search with url', function(){ it('should find album by url', function* (){ - let result = yield lookup('https://play.google.com/music/listen#/album/Bz6wrjczddcj5hurijsv6ohdoay'); + const result = yield lookup('https://play.google.com/music/listen#/album/Bz6wrjczddcj5hurijsv6ohdoay'); result.name.should.equal('Phase 5'); }); }); diff --git a/test/services/deezer.js b/test/services/deezer.js index 136ee32..853506c 100644 --- a/test/services/deezer.js +++ b/test/services/deezer.js @@ -1,27 +1,27 @@ import 'should'; -import deezer from '../../lib/services/deezer'; +import * as deezer from '../../lib/services/deezer'; describe('Deezer', function(){ describe('lookupId', function(){ it('should find album by ID', function* (){ - let result = yield deezer.lookupId('302127', 'album'); + const result = yield deezer.lookupId('302127', 'album'); result.name.should.equal('Discovery'); }); it('should find track by ID', function* (){ - let result = yield deezer.lookupId('3135554', 'track'); + const result = yield deezer.lookupId('3135554', 'track'); result.name.should.equal('Aerodynamic'); }); }); describe('search', function(){ it('should find album by search', function* (){ - let result = yield deezer.search({type: 'album', artist: {name: 'David Guetta'}, name: 'Listen (Deluxe)'}); + const 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* (){ - let result = yield deezer.search({type: 'track', artist: {name: 'Deftones'}, album: {name: 'Deftones'}, name: 'Hexagram'}); + const result = yield deezer.search({type: 'track', artist: {name: 'Deftones'}, album: {name: 'Deftones'}, name: 'Hexagram'}); result.name.should.equal('Hexagram'); }); }); @@ -29,12 +29,12 @@ describe('Deezer', function(){ describe('lookupUrl', function(){ describe('parseUrl', function(){ it('should parse album url into ID', function* (){ - let result = yield deezer.parseUrl('http://www.deezer.com/album/302127'); + const result = yield deezer.parseUrl('http://www.deezer.com/album/302127'); result.id.should.equal(302127); }); it('should parse track url into ID', function* (){ - let result = yield deezer.parseUrl('http://www.deezer.com/track/3135554'); + const result = yield deezer.parseUrl('http://www.deezer.com/track/3135554'); result.id.should.equal(3135554); }); }); diff --git a/test/services/google.js b/test/services/google.js index 4c57efe..3841294 100644 --- a/test/services/google.js +++ b/test/services/google.js @@ -1,39 +1,39 @@ import 'should'; -import google from '../../lib/services/google'; +import * as google from '../../lib/services/google'; describe('Google Play Music', function(){ describe('lookupId', function(){ it('should find album by ID', function* (){ - let result = yield google.lookupId('Byp6lvzimyf74wxi5634ul4tgam', 'album'); + const result = yield google.lookupId('Byp6lvzimyf74wxi5634ul4tgam', 'album'); result.name.should.equal('Listen (Deluxe)'); }); it('should find track by ID', function* (){ - let result = yield google.lookupId('Tjosptub24g2dft37lforqnudpe', 'track'); + const result = yield google.lookupId('Tjosptub24g2dft37lforqnudpe', 'track'); result.name.should.equal('Cherub Rock'); }); }); describe('search', function(){ it('should find album by search', function* (){ - let result = yield google.search({type: 'album', artist: {name: 'Jamie xx'}, name: 'In Colour'}); + const result = yield google.search({type: 'album', artist: {name: 'Jamie xx'}, name: 'In Colour'}); result.name.should.equal('In Colour'); }); }); describe('lookupUrl', function(){ it('should parse regular url into album ID', function* (){ - let result = yield google.parseUrl('https://play.google.com/music/listen#/album/Byp6lvzimyf74wxi5634ul4tgam/Jamie+xx/In+Colour'); + const result = yield google.parseUrl('https://play.google.com/music/listen#/album/Byp6lvzimyf74wxi5634ul4tgam/Jamie+xx/In+Colour'); result.id.should.equal('Byp6lvzimyf74wxi5634ul4tgam'); }); it('should parse url without ID into album ID', function* (){ - let result = yield google.parseUrl('https://play.google.com/music/listen#/album//Jamie+xx/In+Colour'); + const result = yield google.parseUrl('https://play.google.com/music/listen#/album//Jamie+xx/In+Colour'); result.id.should.equal('Bvfmezcj3n42lo4xeuslpclbyrm'); }); it('should parse share url into album ID', function* (){ - let result = yield google.parseUrl('https://play.google.com/music/m/Byp6lvzimyf74wxi5634ul4tgam'); + const result = yield google.parseUrl('https://play.google.com/music/m/Byp6lvzimyf74wxi5634ul4tgam'); result.id.should.equal('Byp6lvzimyf74wxi5634ul4tgam'); }); }); diff --git a/test/services/itunes.js b/test/services/itunes.js index c5c5191..f129311 100644 --- a/test/services/itunes.js +++ b/test/services/itunes.js @@ -1,27 +1,27 @@ import 'should'; -import itunes from '../../lib/services/itunes'; +import * as itunes from '../../lib/services/itunes'; describe('iTunes Music', function(){ describe('lookupId', function(){ it('should find album by ID', function* (){ - let result = yield itunes.lookupId('id215206912', 'album'); + const result = yield itunes.lookupId('id215206912', 'album'); result.name.should.equal('Peace Orchestra'); }); it('should find track by ID', function* (){ - let result = yield itunes.lookupId('id215206958', 'track'); + const result = yield itunes.lookupId('id215206958', 'track'); result.name.should.equal('Double Drums'); }); }); describe('search', function(){ it('should find album by search', function* (){ - let result = yield itunes.search({type: 'album', artist: {name: 'Deftones'}, name: 'Deftones'}); + const result = yield itunes.search({type: 'album', artist: {name: 'Deftones'}, name: 'Deftones'}); result.name.should.equal('Deftones'); }); it('should find track by search', function* (){ - let result = yield itunes.search({type: 'track', artist: {name: 'Deftones'}, album: {name: 'Deftones'}, name: 'Hexagram'}); + const result = yield itunes.search({type: 'track', artist: {name: 'Deftones'}, album: {name: 'Deftones'}, name: 'Hexagram'}); result.name.should.equal('Hexagram'); }); }); @@ -29,12 +29,12 @@ describe('iTunes Music', function(){ describe('lookupUrl', function(){ describe('parseUrl', function(){ it('should parse album url into ID', function* (){ - let result = yield itunes.parseUrl('https://itunes.apple.com/us/album/double-drums/id215206912'); + const 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* (){ - let result = yield itunes.parseUrl('https://itunes.apple.com/us/album/double-drums/id215206912?i=215206958&uo=4'); + const result = yield itunes.parseUrl('https://itunes.apple.com/us/album/double-drums/id215206912?i=215206958&uo=4'); result.id.should.equal('us215206958'); }); }); diff --git a/test/services/rdio.js b/test/services/rdio.js index 26186dd..5dd9a4c 100644 --- a/test/services/rdio.js +++ b/test/services/rdio.js @@ -4,26 +4,26 @@ import * as rdio from '../../lib/services/rdio'; describe('Rdio', function(){ describe('lookupId', function(){ it('should find album by ID', function* (){ - let result = yield rdio.lookupId('Qj4NXr0'); + const result = yield rdio.lookupId('Qj4NXr0'); result.name.should.equal('Listen (Deluxe)'); }); }); describe('search', function(){ it('should find album by search', function* (){ - let result = yield rdio.search({type: 'album', artist: {name: 'David Guetta'}, name: 'Listen (Deluxe)'}); + const 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* (){ - let result = yield rdio.parseUrl('https://www.rdio.com/artist/David_Guetta/album/Listen_(Deluxe)/'); + const 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* (){ - let result = yield rdio.parseUrl('http://rd.io/x/Qj4NXr0/'); + const result = yield rdio.parseUrl('http://rd.io/x/Qj4NXr0/'); result.name.should.equal('Listen (Deluxe)'); }); }); diff --git a/test/services/spotify.js b/test/services/spotify.js index 63d8800..f2d291c 100644 --- a/test/services/spotify.js +++ b/test/services/spotify.js @@ -1,29 +1,29 @@ import 'should'; -import spotify from '../../lib/services/spotify'; +import * as spotify from '../../lib/services/spotify'; describe('Spotify', function(){ describe('lookupId', function(){ it('should find album by ID', function* (){ - let result = yield spotify.lookupId('77UW17CZFyCaRLHdHeofZu', 'album'); + const result = yield spotify.lookupId('77UW17CZFyCaRLHdHeofZu', 'album'); result.name.should.equal('Listen (Deluxe)'); }); it('should find track by ID', function* (){ - let result = yield spotify.lookupId('7dS5EaCoMnN7DzlpT6aRn2', 'track'); + const result = yield spotify.lookupId('7dS5EaCoMnN7DzlpT6aRn2', 'track'); result.name.should.equal('Take Me To Church'); }); }); describe('search', function(){ it('should find album by search', function* (){ - let result = yield spotify.search({type: 'album', artist: {name: 'David Guetta'}, name: 'Listen (Deluxe)'}); + const 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* (){ - let result = yield spotify.parseUrl('https://play.spotify.com/album/77UW17CZFyCaRLHdHeofZu'); + const result = yield spotify.parseUrl('https://play.spotify.com/album/77UW17CZFyCaRLHdHeofZu'); result.id.should.equal('77UW17CZFyCaRLHdHeofZu'); }); }); diff --git a/test/services/xbox.js b/test/services/xbox.js index 0ece79f..cff2fa3 100644 --- a/test/services/xbox.js +++ b/test/services/xbox.js @@ -1,29 +1,29 @@ import 'should'; -import xbox from '../../lib/services/xbox'; +import * as xbox from '../../lib/services/xbox'; describe('Xbox Music', function(){ describe('lookupId', function(){ it('should find album by ID', function* (){ - let result = yield xbox.lookupId('music.8b558d00-0100-11db-89ca-0019b92a3933', 'album'); + const 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* (){ - let result = yield xbox.lookupId('music.8f558d00-0100-11db-89ca-0019b92a3933', 'track'); + const 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* (){ - let result = yield xbox.search({type: 'album', artist: {name: 'Kyuss'}, name: 'Muchas Gracias: The Best Of Kyuss'}); + const 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* (){ - let result = yield xbox.parseUrl('https://music.xbox.com/album/kyuss/muchas-gracias-the-best-of-kyuss/8b558d00-0100-11db-89ca-0019b92a3933'); + const 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'); }); }); diff --git a/test/services/youtube.js b/test/services/youtube.js index e2fc2b1..a33ef79 100644 --- a/test/services/youtube.js +++ b/test/services/youtube.js @@ -1,10 +1,10 @@ import 'should'; -import youtube from '../../lib/services/youtube'; +import * as youtube from '../../lib/services/youtube'; describe('Youtube', function(){ describe('search', function(){ 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'}); + const result = yield youtube.search({type: 'track', artist: {name: 'Aesop Rock'}, album: {name: 'Skeconsthon'}, name: 'Zero Dark Thirty'}); result.name.should.equal('Aesop Rock - Zero Dark Thirty'); }); }); diff --git a/views/app.js b/views/app.js index 8c930c5..9699616 100644 --- a/views/app.js +++ b/views/app.js @@ -1,12 +1,12 @@ import React from 'react'; -import Router, {Route, DefaultRoute, NotFoundRoute, RouteHandler} from 'react-router'; -import ga, {Initializer as GAInitiailizer} from 'react-google-analytics'; +import Router, { Route, DefaultRoute, NotFoundRoute, RouteHandler } from 'react-router'; +import ga, { Initializer as GAInitiailizer } from 'react-google-analytics'; import Home from './home'; import Share from './share'; import Head from './head'; import ErrorView from './error'; -let App = React.createClass({ +const App = React.createClass({ render: function () { return ( @@ -24,7 +24,7 @@ let App = React.createClass({ } }); -let routes = ( +const routes = ( @@ -45,4 +45,4 @@ if (typeof window !== 'undefined') { ga('send', 'pageview'); } -export {routes}; +export { routes }; diff --git a/views/head.js b/views/head.js index 00275ca..c8ec0c8 100644 --- a/views/head.js +++ b/views/head.js @@ -1,5 +1,5 @@ import React from 'react'; -import {State} from 'react-router'; +import { State } from 'react-router'; export default React.createClass({ @@ -8,9 +8,9 @@ export default React.createClass({ router: React.PropTypes.func.isRequired }, render: function() { - var image = this.props.shares ? this.props.shares[0].artwork.large : 'https://match.audio/images/logo-512.png'; - var title = this.props.shares ? this.props.shares[0].name + ' by ' + this.props.shares[0].artist.name : 'Match Audio'; - var shareUrl = 'https://match.audio/' + this.getParams().service + '/' + this.getParams().type + '/' + this.getParams().id; + const image = this.props.shares ? this.props.shares[0].artwork.large : 'https://match.audio/images/logo-512.png'; + const title = this.props.shares ? this.props.shares[0].name + ' by ' + this.props.shares[0].artist.name : 'Match Audio'; + const shareUrl = 'https://match.audio/' + this.getParams().service + '/' + this.getParams().type + '/' + this.getParams().id; return ( diff --git a/views/home.js b/views/home.js index 1e77319..dccb4ce 100644 --- a/views/home.js +++ b/views/home.js @@ -1,10 +1,10 @@ import React from 'react'; import request from 'superagent'; -import {Navigation, State, Link} from 'react-router'; +import { Navigation, State, Link } from 'react-router'; import Faq from './faq'; import Foot from './foot'; -var Recent = React.createClass({ +const Recent = React.createClass({ render: function() { return (
@@ -21,7 +21,7 @@ var Recent = React.createClass({ }); -var RecentItem = React.createClass({ +const RecentItem = React.createClass({ render: function() { if (!this.props.item.artwork) { @@ -38,7 +38,7 @@ var RecentItem = React.createClass({ }); -var SearchForm = React.createClass({ +const SearchForm = React.createClass({ mixins: [ Navigation, State ], @@ -50,26 +50,25 @@ var SearchForm = React.createClass({ }, handleSubmit: function(e) { + e.preventDefault(); this.setState({ submitting: true }); - var that = this; - e.preventDefault(); - var url = this.refs.url.getDOMNode().value.trim(); + const url = this.refs.url.getDOMNode().value.trim(); if (!url) { - that.setState({ + this.setState({ submitting: false }); return; } - request.post('/search').send({url: url}).end(function(req, res) { - that.setState({ + request.post('/search').send({url: url}).end((req, res) => { + this.setState({ submitting: false }); if (res.body.error) { - that.setState({error: res.body.error.message}); + this.setState({error: res.body.error.message}); } - that.transitionTo('share', res.body); + this.transitionTo('share', res.body); }); }, @@ -102,7 +101,7 @@ export default React.createClass({ getInitialState: function () { // Use this only on first page load, refresh whenever we navigate back. if (this.props.recents) { - var recents = this.props.recents; + const recents = this.props.recents; delete this.props.recents; return { recents: recents @@ -115,11 +114,11 @@ export default React.createClass({ componentDidMount: function () { if (!this.props.recents) { - request.get('/recent').set('Accept', 'application/json').end(function(err, res) { + request.get('/recent').set('Accept', 'application/json').end((err, res) => { this.setState({ recents: res.body.recents }); - }.bind(this)); + }); } }, diff --git a/views/share.js b/views/share.js index 8e05582..c381d9c 100644 --- a/views/share.js +++ b/views/share.js @@ -1,9 +1,9 @@ import React from 'react'; import request from 'superagent'; -import {State, Link} from 'react-router'; +import { State, Link } from 'react-router'; import Foot from './foot'; -var MusicItem = React.createClass({ +const MusicItem = React.createClass({ render: function() { if (!this.props.item.matched_at) { @@ -88,7 +88,7 @@ export default React.createClass({ }, componentDidMount: function () { - var complete = this.state.shares.length > 0; + let complete = this.state.shares.length > 0; this.state.shares.forEach(function(share) { if (typeof share.matched_at === 'undefined') { @@ -96,9 +96,9 @@ export default React.createClass({ } }); - var getShares = function() { - request.get(this.getPathname() + '.json').end(function(err, res) { - var shares = res.body.shares; + const getShares = () => { + request.get(this.getPathname() + '.json').end((err, res) => { + const shares = res.body.shares; complete = true; shares.forEach(function(share) { if (typeof share.matched_at === 'undefined') { @@ -118,8 +118,8 @@ export default React.createClass({ shareUrl: 'https://match.audio/' + shares[0].service + '/' + shares[0].type + '/' + shares[0].id }); } - }.bind(this)); - }.bind(this); + }); + }; if (!this.state.shares.length) { getShares(); @@ -134,19 +134,19 @@ export default React.createClass({ // Some hacks to pop open the Twitter/Facebook/Google Plus sharing dialogs without using their code. Array.prototype.forEach.call(document.querySelectorAll('.share-dialog'), function(dialog){ - dialog.addEventListener('click', function(event) { - event.preventDefault(); - var w = 845; - var h = 670; - var dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left; - var dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top; + dialog.addEventListener('click', function(e) { + e.preventDefault(); + const w = 845; + const h = 670; + const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left; + const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top; - var width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width; - var height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height; + const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width; + const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height; - var left = ((width / 2) - (w / 2)) + dualScreenLeft; - var top = ((height / 2) - (h / 2)) + dualScreenTop; - var newWindow = window.open(dialog.href, 'Share Music', 'scrollbars=yes, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left); + const left = ((width / 2) - (w / 2)) + dualScreenLeft; + const top = ((height / 2) - (h / 2)) + dualScreenTop; + const newWindow = window.open(dialog.href, 'Share Music', 'scrollbars=yes, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left); if (window.focus) { newWindow.focus(); } @@ -183,9 +183,9 @@ export default React.createClass({
- {this.state.shares.map(function(item, i){ + {this.state.shares.map((item, i) => { return (); - }.bind(this))} + })}