Browse Source

Mostly finished Playlists. Still a few bugs, improvements to be made.

theflametrooper 8 years ago
parent
commit
0c62bb6c1e

+ 109 - 9
backend/logic/actions/playlists.js

@@ -49,9 +49,9 @@ module.exports = {
 			}
 
 		], (err, playlist) => {
-			if (err) {console.log(err); return cb({ 'status': 'failure', 'message': 'Something went wrong'});}
+			if (err) return cb({ 'status': 'failure', 'message': 'Something went wrong'});
 			cache.pub('playlist.create', data._id);
-			return cb(null, { 'status': 'success', 'message': 'Successfully created playlist' });
+			return cb({ 'status': 'success', 'message': 'Successfully created playlist' });
 		});
 	},
 
@@ -71,23 +71,123 @@ module.exports = {
 		});
 	},
 
-	addSongToPlaylist: (session, _id, song, cb) => {
-		db.models.playlist.findOneAndUpdate({ _id }, { songs: songs.push(song) }, { upsert: true }, (err, data) => {
+	addSongToPlaylist: (session, songId, playlistId, cb) => {
+		async.waterfall([
+			(next) => {
+				utils.getSongFromYouTube(songId, (song) => {
+					song.artists = [];
+					song.genres = [];
+					song.skipDuration = 0;
+					song.thumbnail = 'empty';
+					song.explicit = false;
+					song.requestedBy = 'temp';
+					song.requestedAt = Date.now();
+					next(null, song);
+				});
+			},
+			(newSong, next) => {
+				utils.getSongFromSpotify(newSong, (song) => {
+					next(null, song);
+				});
+			},
+			(newSong, next) => {
+				db.models.playlist.findOne({ _id: playlistId }, (err, playlist) => {
+					if (err) throw err;
+
+					playlist.songs.push(newSong);
+					playlist.save(err => {
+						if (err) {
+							console.error(err);
+							return next('Failed to add song to playlist');
+						}
+
+						cache.hset('playlists', playlistId, playlist);
+						next(null, playlist);
+					});
+				});
+			}
+		],
+		(err, playlist) => {
+			if (err) return cb({ status: 'error', message: err });
+			else return cb({ status: 'success', message: 'Song has been successfully added to the playlist', data: playlist.songs });
+		});
+	},
+
+	removeSongFromPlaylist: (session, songId, playlistId, cb) => {
+		db.models.playlist.findOne({ _id: playlistId }, (err, playlist) => {
 			if (err) throw err;
-			console.log(data, 222);
-			return cb({ status: 'success', message: 'Song has been successfully added to the Playlist', data });
+
+			for (let z = 0; z < playlist.songs.length; z++) {
+				if (playlist.songs[z]._id == songId) playlist.songs.shift(playlist.songs[z]);
+			}
+
+			playlist.save(err => {
+				if (err) {
+					console.error(err);
+					return next('Failed to remove song to playlist');
+				}
+
+				cache.hset('playlists', playlistId, playlist);
+				return cb({ status: 'success', message: 'Song has been successfully removed from playlist', data: playlist.songs });
+			});
 		});
 	},
 
 	updateDisplayName: (session, _id, displayName, cb) => {
-		db.models.playlist.findOneAndUpdate({ _id }, { displayName }, { upsert: true }, (err, data) => {
+		db.models.playlist.findOneAndUpdate({ _id }, { displayName }, { upsert: true }, (err, res) => {
 			if (err) throw err;
-			return cb({ status: 'success', message: 'Playlist has been successfully updated', data });
+			cache.hset('playlists', _id, res);
+			return cb({ status: 'success', message: 'Playlist has been successfully updated' });
+		});
+	},
+
+	promoteSong: (session, playlistId, fromIndex, cb) => {
+		db.models.playlist.findOne({ _id: playlistId }, (err, playlist) => {
+			if (err) throw err;
+
+			let song = playlist.songs[fromIndex];
+			playlist.songs.splice(fromIndex, 1);
+			playlist.songs.splice((fromIndex + 1), 0, song);
+
+			playlist.save(err => {
+				if (err) {
+					console.error(err);
+					return next('Failed to promote song');
+				}
+
+				cache.hset('playlists', playlistId, playlist);
+				return cb({ status: 'success', data: playlist.songs });
+			});
+		});
+	},
+
+	demoteSong: (session, playlistId, fromIndex, cb) => {
+		db.models.playlist.findOne({ _id: playlistId }, (err, playlist) => {
+			if (err) throw err;
+
+			let song = playlist.songs[fromIndex];
+			playlist.songs.splice(fromIndex, 1);
+			playlist.songs.splice((fromIndex - 1), 0, song);
+
+			playlist.save(err => {
+				if (err) {
+					console.error(err);
+					return next('Failed to demote song');
+				}
+
+				cache.hset('playlists', playlistId, playlist);
+				return cb({ status: 'success', data: playlist.songs });
+			});
 		});
 	},
 
 	remove: (session, _id, cb) => {
-		db.models.playlist.remove({ _id });
+		db.models.playlist.remove({ _id }).exec(err => {
+			if (err) throw err;
+		});
+		cache.hdel('playlists', _id, () => {
+			return cb({ status: 'success', message: 'Playlist successfully removed' });
+		});
 	}
 
 };

+ 2 - 42
backend/logic/actions/queueSongs.js

@@ -91,48 +91,8 @@ module.exports = {
 				});
 			},
 			(newSong, next) => {
-				const spotifyParams = [
-					`q=${encodeURIComponent(newSong.title)}`,
-					`type=track`
-				].join('&');
-
-				request(`https://api.spotify.com/v1/search?${spotifyParams}`, (err, res, body) => {
-
-					if (err) {
-						console.error(err);
-						return next('Failed to find song from Spotify');
-					}
-
-					body = JSON.parse(body);
-
-					durationArtistLoop:
-					for (let i in body) {
-						let items = body[i].items;
-						for (let j in items) {
-
-							let item = items[j];
-							let hasArtist = false;
-							for (let k = 0; k < item.artists.length; k++) {
-								let artist = item.artists[k];
-								if (newSong.title.indexOf(artist.name) !== -1) {
-									hasArtist = true;
-								}
-							}
-							if (hasArtist && newSong.title.indexOf(item.name) !== -1) {
-								newSong.duration = item.duration_ms / 1000;
-								newSong.artists = item.artists.map(artist => {
-									return artist.name;
-								});
-								newSong.title = item.name;
-								newSong.explicit = item.explicit;
-								newSong.thumbnail = item.album.images[1].url;
-								break durationArtistLoop;
-							}
-
-						}
-					}
-
-					next(null, newSong);
+				utils.getSongFromSpotify(newSong, (song) => {
+					next(null, song);
 				});
 			},
 			(newSong, next) => {

+ 38 - 0
backend/logic/utils.js

@@ -249,6 +249,44 @@ module.exports = {
 				title: body.items[0].snippet.title,
 				duration
 			};
+			cb(song);
+		});
+	},
+	getSongFromSpotify: (song, cb) => {
+		const spotifyParams = [
+			`q=${encodeURIComponent(song.title)}`,
+			`type=track`
+		].join('&');
+
+		request(`https://api.spotify.com/v1/search?${spotifyParams}`, (err, res, body) => {
+
+			if (err) console.error(err);
+
+			body = JSON.parse(body);
+
+			durationArtistLoop:
+			for (let i in body) {
+				let items = body[i].items;
+				for (let j in items) {
+					let item = items[j];
+					let hasArtist = false;
+					for (let k = 0; k < item.artists.length; k++) {
+						let artist = item.artists[k];
+						if (song.title.indexOf(artist.name) !== -1) hasArtist = true;
+					}
+					if (hasArtist && song.title.indexOf(item.name) !== -1) {
+						song.duration = item.duration_ms / 1000;
+						song.artists = item.artists.map(artist => {
+							return artist.name;
+						});
+						song.title = item.name;
+						song.explicit = item.explicit;
+						song.thumbnail = item.album.images[1].url;
+						break durationArtistLoop;
+					}
+				}
+			}
+
 			cb(song);
 		});
 	}

+ 50 - 11
frontend/components/Modals/Playlists/Edit.vue

@@ -7,19 +7,25 @@
 				<button class='delete' @click='$parent.toggleModal("editPlaylist")'></button>
 			</header>
 			<section class='modal-card-body'>
-				<aside class='menu'>
+				<aside class='menu' v-if='playlist.songs.length > 0'>
 					<ul class='menu-list'>
-						<li v-for='song in playlist.songs'>
-							<a :href='' target='_blank'>{{ song.title }}</a>
+						<li v-for='song in playlist.songs' track-by='$index'>
+							<a :href='' target='_blank'>{{ song.title }} - {{ song.artists.join(', ') }}</a>
 							<div class='controls'>
-								<a href='#' @click=''><i class='material-icons'>keyboard_arrow_down</i></a>
-								<a href='#' @click=''><i class='material-icons'>keyboard_arrow_up</i></a>
+								<a href='#'>
+									<i class='material-icons' v-if='playlist.songs[0] !== song' @click='promoteSong($index)'>keyboard_arrow_up</i>
+									<i class='material-icons' v-else>error</i>
+								</a>
+								<a href='#' @click=''>
+									<i class='material-icons' v-if='playlist.songs.length - 1 !== $index' @click='demoteSong($index)'>keyboard_arrow_down</i>
+									<i class='material-icons' v-else>error</i>
+								</a>
 								<a href='#' @click='removeSongFromPlaylist(song._id)'><i class='material-icons'>delete</i></a>
 							</div>
 						</li>
 					</ul>
+					<br />
 				</aside>
-				<br />
 				<div class='control is-grouped'>
 					<p class='control is-expanded'>
 						<input class='input' type='text' placeholder='Search for Song to add' v-model='songQuery'>
@@ -62,7 +68,7 @@
 				</div>
 			</section>
 			<footer class='modal-card-foot'>
-				<a class='button is-danger' @click=''>Delete Playlist</a>
+				<a class='button is-danger' @click='removePlaylist()'>Remove Playlist</a>
 			</footer>
 		</div>
 	</div>
@@ -82,7 +88,7 @@
 		methods: {
 			searchForSongs: function () {
 				let _this = this;
-				_this.socket.emit('apis.searchYoutube', _this.querySearch, res => {
+				_this.socket.emit('apis.searchYoutube', _this.songQuery, res => {
 					if (res.status == 'success') {
 						_this.songQueryResults = [];
 						for (let i = 0; i < res.data.items.length; i++) {
@@ -97,15 +103,48 @@
 				});
 			},
 			addSongToPlaylist: function (id) {
-				this.socket.emit('playlists.addSongToPlaylist', id, res => {
-					if (res.status == 'success') Toast.methods.addToast(res.message, 3000);
+				let _this = this;
+				_this.socket.emit('playlists.addSongToPlaylist', id, _this.playlist._id, res => {
+					if (res.status == 'success') {
+						Toast.methods.addToast(res.message, 3000);
+						_this.playlist.songs = res.data;
+					}
+				});
+			},
+			removeSongFromPlaylist: function (id) {
+				let _this = this;
+				this.socket.emit('playlists.removeSongFromPlaylist', id, _this.playlist._id, res => {
+					if (res.status == 'success') {
+						Toast.methods.addToast(res.message, 3000);
+						_this.playlist.songs = res.data;
+					}
 				});
 			},
-			removeSongFromPlaylist: function (id) {},
 			renamePlaylist: function () {
 				this.socket.emit('playlists.updateDisplayName', this.playlist._id, this.playlist.displayName, res => {
 					if (res.status == 'success') Toast.methods.addToast(res.message, 3000);
 				});
+			},
+			removePlaylist: function () {
+				let _this = this;
+				_this.socket.emit('playlists.remove', _this.playlist._id, res => {
+					if (res.status == 'success') {
+						Toast.methods.addToast(res.message, 3000);
+						_this.$parent.toggleModal('editPlaylist');
+					}
+				});
+			},
+			promoteSong: function (fromIndex) {
+				let _this = this;
+				_this.socket.emit('playlists.promoteSong', _this.playlist._id, fromIndex, res => {
+					if (res.status == 'success') _this.$set('playlist.songs', res.data); // bug: v-for is not updating
+				});
+			},
+			demoteSong: function (fromIndex) {
+				let _this = this;
+				_this.socket.emit('playlists.demoteSong', _this.playlist._id, fromIndex, res => {
+					if (res.status == 'success') _this.$set('playlist.songs', res.data); // bug: v-for is not updating
+				});
 			}
 		},
 		ready: function () {

+ 1 - 0
frontend/components/Sidebars/Playlist.vue

@@ -34,6 +34,7 @@
 			}
 		},
 		ready: function () {
+			// TODO: Update when playlist is removed/created
 			let _this = this;
 			let socketInterval = setInterval(() => {
 				if (!!_this.$parent.$parent.socket) {