diff --git a/lib/channel.js b/lib/channel.js
index 77788b2c..2f58cb0d 100644
--- a/lib/channel.js
+++ b/lib/channel.js
@@ -9,6 +9,7 @@ var MakeEmitter = require("./emitter");
var InfoGetter = require("./get-info");
var ChatCommand = require("./chatcommand");
var XSS = require("./xss");
+var Media = require("./media").Media;
var fs = require("fs");
var path = require("path");
@@ -452,6 +453,9 @@ Channel.prototype.preJoin = function (user, password) {
if (err) {
user.rank = user.global_rank;
} else {
+ if (!self.registered && user.rank > 2 && rank <= 2) {
+ return;
+ }
user.rank = Math.max(rank, user.global_rank);
}
@@ -515,6 +519,21 @@ Channel.prototype.join = function (user) {
}
}
+ if (!self.registered) {
+ var hasAdmin = false;
+ for (var i = 0; i < self.users.length; i++) {
+ if (self.users[i].rank > 2) {
+ hasAdmin = true;
+ break;
+ }
+ }
+
+ if (!hasAdmin) {
+ user.rank = 4;
+ user.socket.emit("rank", 4);
+ }
+ }
+
self.sendUserJoin(self.users, user);
self.sendUserlist([user]);
};
@@ -524,11 +543,20 @@ Channel.prototype.join = function (user) {
user.socket.join(self.uniqueName);
user.channel = self;
+ if (!self.registered) {
+ user.socket.emit("channelNotRegistered");
+ }
+
self.users.push(user);
self.sendVoteskipUpdate(self.users);
self.sendUsercount(self.users);
user.whenLoggedIn(function () {
+ if (!self.registered) {
+ afterLogin();
+ return;
+ }
+
db.channels.isNameBanned(self.name, user.name, function (err, banned) {
if (!err && banned) {
user.kick("You're banned!");
@@ -554,6 +582,11 @@ Channel.prototype.join = function (user) {
Logger.syslog.log(user.ip + " joined channel " + self.name);
};
+ if (!self.registered) {
+ afterIPBan();
+ return;
+ }
+
db.channels.isIPBanned(self.name, user.ip, function (err, banned) {
if (!err && banned) {
user.kick("You're banned!");
@@ -595,6 +628,19 @@ Channel.prototype.part = function (user) {
self.users.splice(idx, 1);
}
+ if (!self.registered && user.rank === 4) {
+ if (self.users.length > 0) {
+ for (var i = 0; i < self.users.length; i++) {
+ self.users[i].rank = 4;
+ self.users[i].socket.emit("rank", 4);
+ self.sendAll("setUserRank", {
+ name: self.users[i].name,
+ rank: 4
+ });
+ }
+ }
+ }
+
// A change in usercount might cause a voteskip result to change
self.checkVoteskipPass();
self.sendUsercount(self.users);
@@ -911,6 +957,10 @@ Channel.prototype.banIP = function (actor, ip, name, reason, range) {
Channel.prototype.sendBanlist = function (users) {
var self = this;
+ if (!self.registered) {
+ return;
+ }
+
var bans = [];
var unmaskedbans = [];
db.channels.listBans(self.name, function (err, banlist) {
@@ -954,6 +1004,11 @@ Channel.prototype.sendBanlist = function (users) {
*/
Channel.prototype.sendChannelRanks = function (users) {
var self = this;
+
+ if (!self.registered) {
+ return;
+ }
+
db.channels.allRanks(self.name, function (err, ranks) {
if (err) {
return;
diff --git a/lib/user.js b/lib/user.js
index c6f377fe..fe5a56b7 100644
--- a/lib/user.js
+++ b/lib/user.js
@@ -37,6 +37,10 @@ function User(socket) {
self.initChannelCallbacks();
});
+ self.socket.once("initUserPLCallbacks", function () {
+ self.initUserPLCallbacks();
+ });
+
self.socket.once("initACP", function () {
self.whenLoggedIn(function () {
if (self.global_rank >= 255) {
@@ -274,10 +278,13 @@ User.prototype.initChannelCallbacks = function () {
});
wrapTypecheck("queue", function (data) {
- console.log("queue", data);
self.channel.handleQueue(self, data);
});
+ wrapTypecheck("queuePlaylist", function (data) {
+ self.channel.handleQueuePlaylist(self, data);
+ });
+
wrapTypecheck("setTemp", function (data) {
self.channel.handleSetTemp(self, data);
});
@@ -526,4 +533,8 @@ User.prototype.guestLogin = function (name) {
});
};
+User.prototype.initUserPLCallbacks = function () {
+ require("./userplaylists").init(this);
+};
+
module.exports = User;
diff --git a/lib/userplaylists.js b/lib/userplaylists.js
new file mode 100644
index 00000000..9f9f3822
--- /dev/null
+++ b/lib/userplaylists.js
@@ -0,0 +1,77 @@
+var db = require("./database");
+
+function listPlaylists(user) {
+ db.listUserPlaylists(user.name, function (err, rows) {
+ if (err) {
+ user.socket.emit("errorMsg", {
+ msg: "Database error when attempting to fetch list of playlists"
+ });
+ return;
+ }
+
+ user.socket.emit("listPlaylists", rows);
+ });
+}
+
+function clonePlaylist(user, data) {
+ if (!user.inChannel()) {
+ user.socket.emit("errorMsg", {
+ msg: "You must be in a channel in order to clone its playlist"
+ });
+ return;
+ }
+
+ if (typeof data.name !== "string") {
+ return;
+ }
+
+ var pl = user.channel.playlist.items.toArray();
+ db.saveUserPlaylist(pl, user.name, data.name, function (err, res) {
+ if (err) {
+ user.socket.emit("errorMsg", {
+ msg: "Database error when saving playlist"
+ });
+ } else {
+ listPlaylists(user);
+ }
+ });
+}
+
+function deletePlaylist(user, data) {
+ if (typeof data.name !== "string") {
+ return;
+ }
+
+ db.deleteUserPlaylist(user.name, data.name, function (err) {
+ if (err) {
+ user.socket.emit("errorMsg", {
+ msg: err
+ });
+ return;
+ }
+
+ setImmediate(function () {
+ listPlaylists(user);
+ });
+ });
+}
+
+module.exports.init = function (user) {
+ console.log('Initializing playlists for ' + user.name);
+ var s = user.socket;
+ var wrap = function (cb) {
+ return function (data) {
+ if (!user.loggedIn || user.rank < 1) {
+ s.emit("errorMsg", {
+ msg: "You must be logged in to manage playlists"
+ });
+ return;
+ }
+ cb(user, data);
+ };
+ };
+
+ s.on("listPlaylists", wrap(listPlaylists));
+ s.on("clonePlaylist", wrap(clonePlaylist));
+ s.on("deletePlaylist", wrap(deletePlaylist));
+};
diff --git a/templates/channel.jade b/templates/channel.jade
index bc9470e2..2a62a1fa 100644
--- a/templates/channel.jade
+++ b/templates/channel.jade
@@ -56,6 +56,8 @@ html(lang="en")
span.glyphicon.glyphicon-plus
button#showcustomembed.btn.btn-sm.btn-default(title="Embed a custom frame", data-toggle="collapse", data-target="#customembed")
span.glyphicon.glyphicon-th-large
+ button#showplaylistmanager.btn.btn-sm.btn-default(title="Manage playlists", data-toggle="collapse", data-target="#playlistmanager")
+ span.glyphicon.glyphicon-list
button#clearplaylist.btn.btn-sm.btn-default(title="Clear the playlist")
span.glyphicon.glyphicon-trash
button#shuffleplaylist.btn.btn-sm.btn-default(title="Shuffle the playlist")
@@ -75,15 +77,6 @@ html(lang="en")
#leftpane-inner.row
#pollwrap.col-lg-12.col-md-12
#playlistmanagerwrap.col-lg-12.col-md-12
- button#showplaylistmanager.btn.btn-default.btn-block(data-toggle="collapse", data-target="#playlistmanager") Playlist Manager
- #playlistmanager.collapse
- .row.vertical-spacer
- .col-lg-12.col-md-12
- .input-group
- input#userpl_name.form-control(type="text", placeholder="Playlist Name")
- span.input-group-btn
- button#userpl_save.btn.btn-default Save
- ul#userpl_list.col-lg-12.col-md-12
#rightpane.col-lg-7.col-md-7
#rightpane-inner.row
#addfromurl.collapse.plcontrol-collapse.col-lg-12.col-md-12
@@ -114,6 +107,13 @@ html(lang="en")
| Paste the embed code below and click Next or At End.
| Acceptable embed codes are <iframe>
and <object>
tags.
textarea#customembed-content.input-block-level.form-control(rows="3")
+ #playlistmanager.collapse.plcontrol-collapse.col-lg-12.col-md-12
+ .vertical-spacer
+ .input-group
+ input#userpl_name.form-control(type="text", placeholder="Playlist Name")
+ span.input-group-btn
+ button#userpl_save.btn.btn-default Save
+ ul#userpl_list.videolist
#queuefail.col-lg-12.col-md-12
.vertical-spacer
.col-lg-12.col-md-12
diff --git a/www/assets/js/callbacks.js b/www/assets/js/callbacks.js
index 6cb90202..98b87be9 100644
--- a/www/assets/js/callbacks.js
+++ b/www/assets/js/callbacks.js
@@ -143,20 +143,20 @@ Callbacks = {
channelNotRegistered: function() {
var div = $("