Skip to content

Commit

Permalink
Merge cc8ca2d into a02cf21
Browse files Browse the repository at this point in the history
  • Loading branch information
martindale committed Mar 19, 2015
2 parents a02cf21 + cc8ca2d commit 1ffa91b
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 87 deletions.
198 changes: 124 additions & 74 deletions controllers/playlists.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,95 +196,145 @@ module.exports = {
}

},
syncSetup: function(req, res, next) {
import: function() {

if (!req.user) return res.redirect('/login');
if (!req.user.profiles || !req.user.profiles.spotify) return res.redirect('/auth/spotify');
if (!req.user.profiles.spotify.token) return res.redirect('/auth/spotify');
//if (req.user.profiles.spotify.expires < Date.now()) return res.redirect('/auth/spotify');

// stub for spotify API auth
var spotify = {
get: function( path ) {
return rest.get('https://api.spotify.com/v1/' + path , {
headers: {
'Authorization': 'Bearer ' + req.user.profiles.spotify.token
}
});
}
}

var playlist = req.param('playlist');
},
syncAndImport: function(req, res, next) {
if (!req.user.profiles) req.user.profiles = {};

var querySources = ['youtube', 'spotify'];
if (req.param('sourceName')) querySources = [ req.param('sourceName') ];

if (~querySources.indexOf('youtube') && (!req.user.profiles.google || !req.user.profiles.google.token)) return done('no creds');
if (~querySources.indexOf('spotify') && (!req.user.profiles.spotify || !req.user.profiles.spotify.token)) return done('no creds');

var playlist = req.param('playlist');
if (playlist) {
try {
playlist = JSON.parse( playlist );
} catch (e) {
return res.render('500');
}

var url = 'users/' + playlist.user + '/playlists/' + playlist.id + '?limit=250';
spotify.get( url ).on('complete', function(spotifyPlaylist , response ) {
if (!spotifyPlaylist || response.statusCode !== 200) {
req.flash('error', 'Could not retrieve list from Spotify. ' + response.statusCode );

switch (playlist.source) {
default:
req.flash('error', 'Unknown playlist source "'+playlist.source+'"');
return res.redirect('back');
}

console.log('spotifyPlaylist', spotifyPlaylist);
console.log('will be public: ', spotifyPlaylist.public);

var tracks = spotifyPlaylist.tracks.items.map(function(x) {
return {
title: x.track.name,
artist: x.track.artists[0].name,
credits: x.track.artists.map(function(y) {
return y.name
}),
duration: x.track.duration_ms / 1000
}
});
break;
case 'youtube':
req.youtube.get('playlistItems?playlistId='+playlist.id+'&part=contentDetails&maxResults=50').on('complete', function(data) {

var pullers = data.items.map(function( track ) {
return function( trackComplete ) {
req.soundtrack.trackFromSource('youtube', track.contentDetails.videoId , trackComplete );
}
});

var pushers = [];
tracks.forEach(function(track) {
pushers.push(function(done) {
req.soundtrack.trackFromSource('object', track , done );
});
});

async.series( pushers , function(err, tracks) {

tracks.forEach(function(track) {
/* req.app.agency.publish('track:crawl', {
id: track._id
}, function(err) {
console.log('track crawled, doing stuff in initiator');
}); */
async.series( pullers , function(err, results) {
var createdPlaylist = new Playlist({
name: playlist.name,
public: true,
_creator: req.user._id,
_owner: req.user._id,
_tracks: results.map(function(x) { return x._id; })
});
createdPlaylist.save(function(err) {
if (err) console.log(err);
return res.redirect('/' + req.user.slug + '/' + createdPlaylist.slug );
});
});
});

var playlist = new Playlist({
name: spotifyPlaylist.name,
description: spotifyPlaylist.description,
public: spotifyPlaylist.public,
_creator: req.user._id,
_owner: req.user._id,
_tracks: tracks.map(function(x) { return x._id }),
remotes: {
spotify: {
id: spotifyPlaylist.id
}
break;
case 'spotify':
var url = 'users/' + playlist.user + '/playlists/' + playlist.id + '?limit=250';
req.spotify.get( url ).on('complete', function(spotifyPlaylist , response ) {
if (!spotifyPlaylist || response.statusCode !== 200) {
req.flash('error', 'Could not retrieve list from Spotify. ' + response.statusCode );
return res.redirect('back');
}

var tracks = spotifyPlaylist.tracks.items.map(function(x) {
return {
title: x.track.name,
artist: x.track.artists[0].name,
credits: x.track.artists.map(function(y) {
return y.name
}),
duration: x.track.duration_ms / 1000
}
});

var pushers = [];
tracks.forEach(function(track) {
pushers.push(function(done) {
req.soundtrack.trackFromSource('object', track , done );
});
});

async.series( pushers , function(err, tracks) {

tracks.forEach(function(track) {
/* req.app.agency.publish('track:crawl', {
id: track._id
}, function(err) {
console.log('track crawled, doing stuff in initiator');
}); */
});

var playlist = new Playlist({
name: spotifyPlaylist.name,
description: spotifyPlaylist.description,
public: spotifyPlaylist.public,
_creator: req.user._id,
_owner: req.user._id,
_tracks: tracks.map(function(x) { return x._id }),
remotes: {
spotify: {
id: spotifyPlaylist.id
}
}
});
playlist.save(function(err) {
res.redirect('/' + req.user.slug + '/' + playlist.slug );
});
});
});
playlist.save(function(err) {
res.redirect('/' + req.user.slug + '/' + playlist.slug );
});
});
break;
}
return;
}

var stack = {};

async.parallel({
youtube: syncYoutube,
spotify: syncSpotify
}, function(err, results) {
if (~querySources.indexOf('youtube') && !results.youtube) return res.redirect('/auth/google?next=/sets/import');
if (~querySources.indexOf('spotify') && !results.spotify) return res.redirect('/auth/spotify?next=/sets/import');

res.render('sets-import', {
youtube: results.youtube || [],
spotify: results.spotify || [],
});

} else {
spotify.get('users/' + req.user.profiles.spotify.id + '/playlists').on('complete', function(results, response) {
if (response.statusCode == 401) return res.redirect('/auth/spotify');
res.render('sets-import', {
playlists: results.items
});

function syncYoutube( done ) {
req.youtube.get('playlists?part=snippet&mine=true&maxResults=50').on('complete', function(data) {
req.user.profiles.google.playlists = data.items;
req.user.save(function(err) {
done( err , data.items );
});
});
}

function syncSpotify( done ) {
req.spotify.get('users/' + req.user.profiles.spotify.id + '/playlists').on('complete', function(results, response) {
if (!results || response.statusCode == 401) return done('expired');
req.user.profiles.spotify.playlists = results.items;
req.user.save(function(err) {
done( err , results.items );
});
});
}
Expand Down
10 changes: 9 additions & 1 deletion models/Person.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,15 @@ var PersonSchema = new Schema({
username: String,
token: String,
updated: Date,
expires: Number
expires: Number,
playlists: []
},
google: {
id: String,
username: String,
token: String,
updated: Date,
playlists: []
}
}
, preferences: {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"mongoose-agency": "0.0.0",
"mongoose-slug": "~1.3.0",
"passport": "~0.1.17",
"passport-google-oauth": "^0.2.0",
"passport-local": "~0.1.6",
"passport-local-mongoose": "~0.2.4",
"passport-spotify": "^0.1.0",
Expand Down
99 changes: 92 additions & 7 deletions soundtrack.js
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,92 @@ var soundtracker = function(req, res, next) {
next();
};

var externalizer = function(req, res, next) {
if (!req.user) return res.redirect('/login');

req.youtube = {
get: function( path ) {
return rest.get('https://www.googleapis.com/youtube/v3/' + path + '&access_token=' + req.user.profiles.google.token , {
'Authorization': 'Bearer ' + req.user.profiles.google.token
});
}
}

// stub for spotify API auth
req.spotify = {
get: function( path ) {
return rest.get('https://api.spotify.com/v1/' + path , {
headers: {
'Authorization': 'Bearer ' + req.user.profiles.spotify.token
}
});
}
}

return next();
}

var redirectSetup = function(req, res, next) {
if (req.param('next')) {
req.session.next = req.param('next');
req.session.save( next );
} else {
return next();
}
}
var redirectNext = function(req, res, next) {
if (req.session.next) {
var path = req.session.next;
delete req.session.next;
req.session.save(function() {
res.redirect( path );
});
} else {
res.redirect('/');
}
}

if (config.google && config.google.id && config.google.secret) {
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
passport.use(new GoogleStrategy({
clientID: config.google.id,
clientSecret: config.google.secret,
//callbackURL: ((config.app.safe) ? 'https://' : 'http://') + config.app.host + '/auth/google/callback',
callbackURL: 'https://soundtrack.io/auth/google/callback',
scope: 'profile email https://www.googleapis.com/auth/youtube',
passReqToCallback: true
}, function(req, accessToken, refreshToken, profile, done) {

Person.findOne({ $or: [
{ _id: (req.user) ? req.user._id : undefined }
, { 'profiles.google.id': profile.id }
]}).exec(function(err, person) {
console.log('search result: ', err , person );

if (!person) var person = new Person({ username: profile.username });

person.profiles.google = {
id: profile.id,
token: accessToken,
updated: new Date(),
expires: null
}

person.save(function(err) {
if (err) console.log('serious error', err );
done(err, person);
});

});

}));

app.get('/auth/google', redirectSetup , passport.authenticate('google') );
app.get('/auth/google/callback', passport.authenticate('google') , redirectNext );

app.get('/sets/import', soundtracker , externalizer , playlists.syncAndImport );

}

if (config.spotify && config.spotify.id && config.spotify.secret) {
passport.use(new SpotifyStrategy({
Expand Down Expand Up @@ -456,12 +542,10 @@ if (config.spotify && config.spotify.id && config.spotify.secret) {
});
}));

app.get('/auth/spotify', passport.authenticate('spotify') );
app.get('/auth/spotify/callback', passport.authenticate('spotify') , function(req, res) {
res.redirect('/');
});
app.get('/auth/spotify', redirectSetup , passport.authenticate('spotify') );
app.get('/auth/spotify/callback', passport.authenticate('spotify') , redirectNext );

app.get('/sets/sync/spotify', soundtracker , playlists.syncSetup );
app.get('/sets/sync/spotify', soundtracker , externalizer , playlists.syncAndImport );

}

Expand Down Expand Up @@ -868,8 +952,9 @@ Room.find().exec(function(err, rooms) {
return done();
}

app.rooms[ room.slug ].startMusic( errorHandler );

//app.rooms[ room.slug ].startMusic( errorHandler );
app.rooms[ room.slug ].startMusic( done );

});
};
});
Expand Down
20 changes: 17 additions & 3 deletions views/sets-import.jade
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,27 @@ block content
.well.content-well
h1 Import Set

if (playlists.length)

h2 From Spotify...
if (spotify.length)
table.table.tablesorter
for playlist in playlists
for playlist in spotify
tr
td #{playlist.name}
td #{playlist.tracks.total} tracks
td
a.btn(href="/sets/sync/spotify?playlist=#{JSON.stringify({ source: 'spotify', id: playlist.id, user: playlist.owner.id })}") import &raquo;
a.btn(href="/sets/import?playlist=#{JSON.stringify({ source: 'spotify', id: playlist.id, user: playlist.owner.id })}") import &raquo;
else
p You don't have any playlists on Spotify. :(

h2 From YouTube...
if (youtube.length)
table.table.tablesorter
for playlist in youtube
tr
td #{playlist.snippet.title}
//-td #{playlist.tracks.total} tracks
td
a.btn(href="/sets/import?playlist=#{JSON.stringify({ source: 'youtube', id: playlist.id, name: playlist.snippet.title })}") import &raquo;
else
p You don't have any playlists on YouTube. :(

0 comments on commit 1ffa91b

Please sign in to comment.