Initial Commit
This commit is contained in:
commit
f8d3ea92b1
12 changed files with 350 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
node_modules
|
1
Procfile
Normal file
1
Procfile
Normal file
|
@ -0,0 +1 @@
|
|||
web: node ./bin/www
|
66
app.js
Normal file
66
app.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
var express = require('express');
|
||||
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 bodyParser = require('body-parser');
|
||||
|
||||
var routes = require('./routes/index');
|
||||
|
||||
var app = express();
|
||||
|
||||
// view engine setup
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
// uncomment after placing your favicon in /public
|
||||
//app.use(favicon(__dirname + '/public/favicon.ico'));
|
||||
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')));
|
||||
|
||||
app.use('/', routes);
|
||||
|
||||
// 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, next) {
|
||||
res.status(err.status || 500);
|
||||
res.render('error', {
|
||||
message: err.message,
|
||||
error: err
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// production error handler
|
||||
// no stacktraces leaked to user
|
||||
app.use(function(err, req, res, next) {
|
||||
res.status(err.status || 500);
|
||||
res.render('error', {
|
||||
message: err.message,
|
||||
error: {}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
module.exports = app;
|
9
bin/www
Executable file
9
bin/www
Executable file
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env node
|
||||
var debug = require('debug')('unify.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);
|
||||
});
|
59
lib/googleplaymusic.js
Normal file
59
lib/googleplaymusic.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
var parse = require("url").parse;
|
||||
var PlayMusic = require('playmusic');
|
||||
var pm = new PlayMusic();
|
||||
|
||||
if (!process.env.GOOGLE_EMAIL || !process.env.GOOGLE_PASSWORD) {
|
||||
throw new Error("You need to set GOOGLE_EMAIL and GOOGLE_PASSWORD environment variables");
|
||||
}
|
||||
|
||||
// It's probably ok to not wait for this to finish
|
||||
pm.init({email: process.env.GOOGLE_EMAIL, password: process.env.GOOGLE_PASSWORD}, function() {});
|
||||
|
||||
module.exports.lookupId = function(id, type, next) {
|
||||
pm.getAlbum(id, true, function(album) {
|
||||
next({
|
||||
id: album.albumId,
|
||||
name: album.name,
|
||||
url: "https://play.google.com/music/listen#/album/" + album.albumId,
|
||||
artwork: album.albumArtRef.replace("http:", ""),
|
||||
artist: {
|
||||
name: album.artist
|
||||
},
|
||||
type: "album"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.search = function(query, type, next) {
|
||||
pm.search(query, 5, function(data) { // max 5 results
|
||||
var result = data.entries.filter(function(result) {
|
||||
return result.album;
|
||||
}).sort(function(a, b) { // sort by match score
|
||||
return a.score < b.score;
|
||||
}).shift();
|
||||
|
||||
module.exports.lookupId(result.album.albumId, "album", next);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.parseUrl = function(url, next) {
|
||||
// https://play.google.com/music/listen#/album/B3lxthejqxjxja2bhzchcw5qaci
|
||||
// https://play.google.com/music/listen#/album//Underworld/Everything%2C+Everything+(Live)
|
||||
var parsed = parse(url.replace(/\+/g, "%20"));
|
||||
var hash = parsed.hash;
|
||||
|
||||
var matches = hash.match(/\/album[\/]+([^\/]+)\/([^\/]+)/);
|
||||
|
||||
if (matches && matches[2]) {
|
||||
var artist = decodeURIComponent(matches[1]);
|
||||
var album = decodeURIComponent(matches[2]);
|
||||
module.exports.search(artist + " " + album, "album", function(googleAlbum) {
|
||||
next(googleAlbum);
|
||||
})
|
||||
} else {
|
||||
var matches = hash.match(/\/album[\/]+([\w]+)/);
|
||||
if (matches && matches[1]) {
|
||||
return next({id:matches[1], type: "album"})
|
||||
}
|
||||
}
|
||||
}
|
49
lib/spotify.js
Normal file
49
lib/spotify.js
Normal file
|
@ -0,0 +1,49 @@
|
|||
var parse = require('url').parse;
|
||||
var spotify = require('spotify');
|
||||
|
||||
module.exports.lookupId = function(id, type, next) {
|
||||
spotify.lookup({id: id, type: type}, function(err, data) {
|
||||
if ( err ) {
|
||||
console.log('Error occurred: ' + err);
|
||||
return;
|
||||
}
|
||||
|
||||
var artist = data.artists[0];
|
||||
|
||||
next({
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
url: "https://play.spotify.com/album/" + data.id,
|
||||
artwork: data.images[0].url.replace("http:", ""),
|
||||
artist: {
|
||||
name: artist.name
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.search = function(query, type, next) {
|
||||
spotify.search({query: query, type: type}, function(err, data) {
|
||||
if ( err ) {
|
||||
console.log('Error occurred: ' + err);
|
||||
return;
|
||||
}
|
||||
|
||||
album = data.albums.items[0];
|
||||
|
||||
module.exports.lookupId(album.id, "album", next);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.parseUrl = function(url, next) {
|
||||
// https://play.spotify.com/album/3W3ENDBQMJ9bD2qmxWI2f0
|
||||
// https://play.spotify.com/track/3W3ENDBQMJ9bD2qmxWI2f0
|
||||
// https://open.spotify.com/album/3W3ENDBQMJ9bD2qmxWI2f0
|
||||
// https://open.spotify.com/track/3W3ENDBQMJ9bD2qmxWI2f0
|
||||
|
||||
var matches = parse(url).path.match(/\/album[\/]+([^\/]+)/);
|
||||
|
||||
if (matches && matches[1]) {
|
||||
return next({id:matches[1], type: "album"})
|
||||
}
|
||||
}
|
24
package.json
Normal file
24
package.json
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "unify.audio",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node ./bin/www"
|
||||
},
|
||||
"engines": {
|
||||
"node": "0.10.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"body-parser": "~1.8.1",
|
||||
"connect-flash": "^0.1.1",
|
||||
"cookie-parser": "~1.3.3",
|
||||
"debug": "~2.0.0",
|
||||
"ejs": "~0.8.5",
|
||||
"express": "~4.9.0",
|
||||
"express-session": "^1.9.2",
|
||||
"morgan": "~1.3.0",
|
||||
"playmusic": "^1.1.0",
|
||||
"serve-favicon": "~2.1.3",
|
||||
"spotify": "^0.3.0"
|
||||
}
|
||||
}
|
8
public/stylesheets/style.css
Normal file
8
public/stylesheets/style.css
Normal file
|
@ -0,0 +1,8 @@
|
|||
body {
|
||||
padding: 50px;
|
||||
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #00B7FF;
|
||||
}
|
64
routes/index.js
Normal file
64
routes/index.js
Normal file
|
@ -0,0 +1,64 @@
|
|||
var express = require('express');
|
||||
var router = express.Router();
|
||||
|
||||
var googleplaymusic = require('../lib/googleplaymusic');
|
||||
var spotify = require('../lib/spotify');
|
||||
|
||||
//https://play.spotify.com/album/3W3ENDBQMJ9bD2qmxWI2f0
|
||||
//https://play.google.com/music/listen#/album/B3lxthejqxjxja2bhzchcw5qaci
|
||||
|
||||
router.get('/:service/:type/:id', function(req, res) {
|
||||
var service = req.params.service;
|
||||
var type = req.params.type;
|
||||
var id = req.params.id;
|
||||
|
||||
switch(service) {
|
||||
case "spotify":
|
||||
spotify.lookupId(id, type, function(spotifyAlbum) {
|
||||
googleplaymusic.search(spotifyAlbum.artist.name + " " + spotifyAlbum.name, "album", function(googleAlbum) {
|
||||
res.render('album', {googleAlbum: googleAlbum, spotifyAlbum: spotifyAlbum});
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "google":
|
||||
googleplaymusic.lookupId(id, type, function(googleAlbum) {
|
||||
spotify.search(googleAlbum.artist.name + " " + googleAlbum.name, "album", function(spotifyAlbum) {
|
||||
res.render('album', {googleAlbum: googleAlbum, spotifyAlbum: spotifyAlbum});
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/search', function(req, res) {
|
||||
// determine spotify or google music
|
||||
var url = req.body.url;
|
||||
|
||||
if (url.match(/spotify\.com/)) {
|
||||
spotify.parseUrl(url, function(result) {
|
||||
if (!result.id) {
|
||||
req.flash('search-error', 'No match found for this link');
|
||||
res.redirect('/');
|
||||
}
|
||||
res.redirect("/spotify/" + result.type + "/" + result.id);
|
||||
});
|
||||
} else if (url.match(/play\.google\.com/)) {
|
||||
googleplaymusic.parseUrl(url, function(result) {
|
||||
if (!result.id) {
|
||||
req.flash('search-error', 'No match found for this link');
|
||||
res.redirect('/');
|
||||
}
|
||||
res.redirect("/google/" + result.type + "/" + result.id);
|
||||
});
|
||||
} else {
|
||||
req.flash('search-error', 'No match found for this link');
|
||||
res.redirect('/');
|
||||
}
|
||||
});
|
||||
|
||||
/* GET home page. */
|
||||
router.get('/', function(req, res) {
|
||||
res.render('index', { error: req.flash('search-error') });
|
||||
});
|
||||
|
||||
module.exports = router;
|
27
views/album.ejs
Normal file
27
views/album.ejs
Normal file
|
@ -0,0 +1,27 @@
|
|||
<!doctype html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Unify Audio</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
|
||||
<style>
|
||||
.container {
|
||||
padding-top: 100px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<img src="<%= spotifyAlbum.artwork %>" height=200 class="img-rounded" /> <%= spotifyAlbum.id %> <a href="<%= spotifyAlbum.url %>">Play on Spotify</a>
|
||||
</div>
|
||||
<div class="row">
|
||||
<img src="<%= googleAlbum.artwork %>" height=200 class="img-rounded" /> <%= googleAlbum.id %> <a href="<%= googleAlbum.url %>">Play on Google Music</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
3
views/error.ejs
Normal file
3
views/error.ejs
Normal file
|
@ -0,0 +1,3 @@
|
|||
<h1><%= message %></h1>
|
||||
<h2><%= error.status %></h2>
|
||||
<pre><%= error.stack %></pre>
|
39
views/index.ejs
Normal file
39
views/index.ejs
Normal file
|
@ -0,0 +1,39 @@
|
|||
<!doctype html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Unify Audio</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
|
||||
<style>
|
||||
.container {
|
||||
padding-top: 100px;
|
||||
}
|
||||
.url-search {
|
||||
text-align: center;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="row url-search">
|
||||
<%= error %>
|
||||
<form role="form" method="post" action="/search" class="form-inline">
|
||||
<div class="form-group form-group-lg">
|
||||
<input type="text" name="url" placeholder="Paste link here" class="form-control">
|
||||
<input type="submit" class="btn btn-default btn-lg" value="Find Matches">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-sm-offset-3">
|
||||
<p>Make sharing music from subscription services better. Give us one link and we'll match it with other services and give you back a link with all of them. <a href="/google/album/B3qrtsvk5s3piwyla76sk6qyxny">Here's an example</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue