From 3d3d21a3f9cd7bfde17ecd1a81c6eaabbc6344d9 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Sun, 29 Sep 2013 00:27:43 -0500 Subject: [PATCH 01/14] Add asyncqueue class --- lib/asyncqueue.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 lib/asyncqueue.js diff --git a/lib/asyncqueue.js b/lib/asyncqueue.js new file mode 100644 index 00000000..f8b3b38c --- /dev/null +++ b/lib/asyncqueue.js @@ -0,0 +1,45 @@ +var AsyncQueue = function () { + this._q = []; + this._lock = false; + this._tm = 0; +}; + +AsyncQueue.prototype.next = function () { + if (this._q.length > 0) { + if (!this.lock()) + return; + var fn = this._q.shift(); + fn(this); + } +}; + +AsyncQueue.prototype.lock = function () { + if (this._lock) + return false; + + this._lock = true; + return true; +}; + +AsyncQueue.prototype.release = function () { + var self = this; + if (!self._lock) + return false; + + self._lock = false; + process.nextTick(function () { + self.next(); + }); + return true; +}; + +AsyncQueue.prototype.queue = function (fn) { + var self = this; + self._q.push(fn); + self.next(); +}; + +AsyncQueue.prototype.reset = function () { + this._q = []; + this._lock = false; +}; From 21bd2708130f06968fcd464e33b8c270f1f6f8db Mon Sep 17 00:00:00 2001 From: calzoneman Date: Sun, 29 Sep 2013 11:30:36 -0500 Subject: [PATCH 02/14] Improvements to playlist queue --- lib/asyncqueue.js | 17 +++++++++--- lib/get-info.js | 6 +++- lib/playlist.js | 55 ++++++++++++++++++++++++++++++++++++- tests/naokosimulator2013.js | 38 +++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 tests/naokosimulator2013.js diff --git a/lib/asyncqueue.js b/lib/asyncqueue.js index f8b3b38c..2383056c 100644 --- a/lib/asyncqueue.js +++ b/lib/asyncqueue.js @@ -8,14 +8,21 @@ AsyncQueue.prototype.next = function () { if (this._q.length > 0) { if (!this.lock()) return; - var fn = this._q.shift(); + var item = this._q.shift(); + var fn = item[0], tm = item[1]; + this._tm = Date.now() + item[1]; fn(this); } }; AsyncQueue.prototype.lock = function () { - if (this._lock) + if (this._lock) { + if (this._tm > 0 && Date.now() > this._tm) { + this._tm = 0; + return true; + } return false; + } this._lock = true; return true; @@ -27,7 +34,7 @@ AsyncQueue.prototype.release = function () { return false; self._lock = false; - process.nextTick(function () { + setImmediate(function () { self.next(); }); return true; @@ -35,7 +42,7 @@ AsyncQueue.prototype.release = function () { AsyncQueue.prototype.queue = function (fn) { var self = this; - self._q.push(fn); + self._q.push([fn, 20000]); self.next(); }; @@ -43,3 +50,5 @@ AsyncQueue.prototype.reset = function () { this._q = []; this._lock = false; }; + +module.exports = AsyncQueue; diff --git a/lib/get-info.js b/lib/get-info.js index 613dec23..3bbe2ad9 100644 --- a/lib/get-info.js +++ b/lib/get-info.js @@ -23,6 +23,7 @@ module.exports = function (Server) { // This should cut down on needing to restart the server var d = domain.create(); d.on("error", function (err) { + Logger.errlog.log(err.trace()); Logger.errlog.log("urlRetrieve failed: " + err); Logger.errlog.log("Request was: " + options.host + options.path); callback(503, err); @@ -88,7 +89,8 @@ module.exports = function (Server) { callback(true, null); return; } - + + var buffer = data; try { data = JSON.parse(data); var seconds = data.entry.media$group.yt$duration.seconds; @@ -599,6 +601,8 @@ module.exports = function (Server) { getMedia: function (id, type, callback) { if(type in this.Getters) { this.Getters[type](id, callback); + } else { + callback("Unknown media type '" + type + "'", null); } } }; diff --git a/lib/playlist.js b/lib/playlist.js index 1e59657b..38e5d405 100644 --- a/lib/playlist.js +++ b/lib/playlist.js @@ -10,6 +10,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI */ ULList = require("./ullist").ULList; +var AsyncQueue = require("./asyncqueue"); var Media = require("./media").Media; var AllPlaylists = {}; @@ -52,6 +53,7 @@ function Playlist(chan) { }; this.lock = false; this.action_queue = []; + this.fnqueue = new AsyncQueue(); this._qaInterval = false; AllPlaylists[name] = this; @@ -247,7 +249,58 @@ Playlist.prototype.addCachedMedia = function(data, callback) { this.queueAction(action); } -Playlist.prototype.addMedia = function(data, callback) { +Playlist.prototype.addMedia = function (data, cb) { + var self = this; + if (data.type === "yp") { + this.addYouTubePlaylist(data, cb); + return; + } + + this.fnqueue.queue(function (q) { + var afterData = function (m) { + if (data.maxlength && data.seconds > m.maxlength) { + setImmediate(function () { + cb("Media is too long!", null); + }); + q.release(); + return; + } + + var it = self.makeItem(m); + it.queueby = data.queueby; + it.temp = data.temp; + var pos = "append"; + if(data.pos == "next") { + if (!self.current) + pos = "prepend"; + else + pos = self.current.uid; + } + var err = self.add(it, pos); + setImmediate(function () { + cb(err, err ? null : it); + }); + q.release(); + }; + + if (typeof data.title === "string") { + afterData(new Media(data.id, data.title, data.seconds, data.type)); + } else { + self.server.infogetter.getMedia(data.id, data.type, + function (e, m) { + if (e) { + setImmediate(function () { cb(e, null); }); + q.release(); + return; + } + + afterData(m); + }); + } + }); +}; + +Playlist.prototype._addMedia = function(data, callback) { if(data.type == "yp") { this.addYouTubePlaylist(data, callback); diff --git a/tests/naokosimulator2013.js b/tests/naokosimulator2013.js new file mode 100644 index 00000000..d3eb2fdc --- /dev/null +++ b/tests/naokosimulator2013.js @@ -0,0 +1,38 @@ +var fs = require('fs'); +var io = require('socket.io-client'); +var socket = io.connect('http://localhost:1337'); +socket.on('connect', function () { + socket.emit('login', { name: 'test', pw: 'test' }); + socket.emit('joinChannel', { name: 'test' }); +}); + +socket.on('queueFail', function (msg) { + console.log(msg); +}); + +/* Stress test adding a lot of videos in a very short timespan */ + +function testAddVideos() { + var pl = fs.readFileSync('largepl.json') + ''; + pl = JSON.parse(pl); + var ids = []; + for (var i = 0; i < pl.length; i++) { + if (pl[i].type === 'yt') + ids.push(pl[i].id); + } + + for (var i = 0; i < ids.length; i++) { + (function (i) { + setTimeout(function () { + console.log('queue', ids[i]); + socket.emit('queue', { + id: ids[i], + type: 'yt', + pos: 'end' + }); + }, 1050 * i); + })(i); + } +} + +testAddVideos(); From a371ca644049f76fffaee285c1fc373c019d3093 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Sun, 29 Sep 2013 19:53:27 -0500 Subject: [PATCH 03/14] Continue refactoring playlist --- lib/channel.js | 171 +++++++++++++++++++++++------------------------- lib/playlist.js | 21 +++++- 2 files changed, 103 insertions(+), 89 deletions(-) diff --git a/lib/channel.js b/lib/channel.js index 09745372..559957e6 100644 --- a/lib/channel.js +++ b/lib/channel.js @@ -22,6 +22,7 @@ var Playlist = require("./playlist"); var sanitize = require("validator").sanitize; var $util = require("./utilities"); var url = require("url"); +var AsyncQueue = require("./asyncqueue"); var Channel = function(name, Server) { var self = this; @@ -36,6 +37,7 @@ var Channel = function(name, Server) { self.registered = false; self.users = []; self.playlist = new Playlist(self); + self.plqueue = new AsyncQueue(); self.position = -1; self.drinks = 0; self.leader = null; @@ -1371,116 +1373,109 @@ Channel.prototype.addMedia = function(data, user) { data.maxlength = self.hasPermission(user, "exceedmaxlength") ? 0 : this.opts.maxlength; + if (data.pos === "end") + data.pos = "append"; - var postAdd = function (item, cached) { - if (self.dead) + if (data.type === "cu" && data.title) { + var t = data.title; + if(t.length > 100) + t = t.substring(0, 97) + "..."; + data.title = t; + } + + var afterData = function (q, c, m) { + if (data.maxlength && data.seconds > data.maxlength) { + user.socket.emit("queueFail", + "Media is too long!"); + q.release(); return; - - if(item.media.type === "cu" && data.title) { - var t = data.title; - if(t.length > 100) - t = t.substring(0, 97) + "..."; - item.media.title = t; } - self.logger.log("### " + user.name + " queued " + item.media.title); + + m.pos = data.pos; + m.queueby = data.queueby; + m.temp = data.temp; + var res = self.playlist.addMedia(m); + if (res.error) { + user.socket.emit("queueFail", res.error); + q.release(); + return; + } + + var item = res.item; + self.logger.log("### " + user.name + " queued " + + item.media.title); self.sendAll("queue", { item: item.pack(), after: item.prev ? item.prev.uid : "prepend" }); self.broadcastPlaylistMeta(); - if(!cached && !item.temp) + if (!c && !item.temp) self.cacheMedia(item.media); - } + q.release(); + }; - // No need to check library for livestreams - they aren't cached - if(isLive(data.type)) { - self.playlist.addMedia(data, function (err, data) { + // Don't check library for livestreams or if the channel is + // unregistered + if (!self.registered || isLive(data.type)) { + self.plqueue.queue(function (q) { if (self.dead) return; + var cb = afterData.bind(self, q, false); + self.server.infogetter.getMedia(data.id, data.type, + function (e, m) { + if (self.dead) + return; + if (e) { + user.socket.emit("queueFail", e); + q.release(); + return; + } - if(err) { - if(err === true) - err = false; - if(user) - user.socket.emit("queueFail", err); - return; - } - else { - postAdd(data, false); - } + cb(m); + }); }); - return; - } - - // Don't search library if the channel isn't registered - if(!self.registered) { - self.playlist.addMedia(data, function(err, item) { + } else { + self.server.db.getLibraryItem(self.name, data.id, + function (err, item) { if (self.dead) return; - if(err) { - if(err === true) - err = false; - if(user) - user.socket.emit("queueFail", err); + if (err) { + user.socket.emit("queueFail", "Internal error: " + err); return; + } + + if (item !== null) { + if (data.maxlength && item.seconds > data.maxlength) { + user.socket.emit("queueFail", "Media is too long!"); + return; + } + + self.plqueue.queue(function (q) { + if (self.dead) + return; + afterData.bind(self, q, true)(item); + }); } else { - postAdd(item, false); + self.plqueue.queue(function (q) { + if (self.dead) + return; + self.server.infogetter.getMedia(data.id, data.type, + function (e, m) { + if (self.dead) + return; + if (e) { + user.socket.emit("queueFail", e); + q.release(); + return; + } + + afterData.bind(self, q, false)(m); + }); + }); } }); - return; } - self.server.db.getLibraryItem(self.name, data.id, - function (err, item) { - if (self.dead) - return; - - if(err) { - user.socket.emit("queueFail", "Internal error: " + err); - return; - } - - if(item !== null) { - var m = new Media(item.id, item.title, item.seconds, item.type); - if(data.maxlength && m.seconds > data.maxlength) { - user.socket.emit("queueFail", "Media is too long!"); - return; - } - - data.media = m; - self.playlist.addCachedMedia(data, function (err, item) { - if (self.dead) - return; - - if(err) { - if(err === true) - err = false; - if(user) - user.socket.emit("queueFail", err); - return; - } else { - postAdd(item, true); - } - }); - } else { - self.playlist.addMedia(data, function(err, item) { - if (self.dead) - return; - - if(err) { - if(err === true) - err = false; - if(user) - user.socket.emit("queueFail", err); - return; - } else { - postAdd(item, false); - } - }); - } - - }); - } Channel.prototype.addMediaList = function(data, user) { diff --git a/lib/playlist.js b/lib/playlist.js index 38e5d405..337d3730 100644 --- a/lib/playlist.js +++ b/lib/playlist.js @@ -224,6 +224,25 @@ Playlist.prototype.add = function(item, pos) { return false; } +Playlist.prototype.addMedia = function (data) { + var pos = data.pos; + if (pos === "next") { + if (this.current !== null) + pos = this.current.uid; + else + pos = "append" + } + + var m = new Media(data.id, data.title, data.seconds, data.type); + var item = this.makeItem(m); + item.queueby = data.queueby; + item.temp = data.temp; + return { + item: item, + error: this.add(item, data.pos) + }; +}; + Playlist.prototype.addCachedMedia = function(data, callback) { var pos = "append"; if(data.pos == "next") { @@ -249,7 +268,7 @@ Playlist.prototype.addCachedMedia = function(data, callback) { this.queueAction(action); } -Playlist.prototype.addMedia = function (data, cb) { +Playlist.prototype.__addMedia = function (data, cb) { var self = this; if (data.type === "yp") { this.addYouTubePlaylist(data, cb); From 2fc8349dafde3f4aa1203640beb585e19cecbe95 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Sun, 29 Sep 2013 22:32:37 -0500 Subject: [PATCH 04/14] Migrate youtube playlists and user playlists, comma lists sometimes break still --- lib/channel.js | 101 ++++++++++++++++------------ lib/playlist.js | 171 +----------------------------------------------- 2 files changed, 60 insertions(+), 212 deletions(-) diff --git a/lib/channel.js b/lib/channel.js index 559957e6..1f3da550 100644 --- a/lib/channel.js +++ b/lib/channel.js @@ -1315,7 +1315,7 @@ Channel.prototype.tryQueue = function(user, data) { return; } - if(data.pos == "next" && !this.hasPermission(user, "playlistnext")) { + if (data.pos === "next" && !this.hasPermission(user, "playlistnext")) { return; } @@ -1336,16 +1336,20 @@ Channel.prototype.tryQueue = function(user, data) { return; } - if(typeof data.title !== "string") + if (typeof data.title !== "string" || data.type !== "cu") data.title = false; data.queueby = user ? user.name : ""; data.temp = !this.hasPermission(user, "addnontemp"); - if(data.list) - this.addMediaList(data, user); - else + if (data.list) { + for (var i = 0; i < data.list.length; i++) { + data.list[i].pos = data.pos; + this.tryQueue(user, data.list[i]); + } + } else { this.addMedia(data, user); + } } Channel.prototype.addMedia = function(data, user) { @@ -1396,6 +1400,7 @@ Channel.prototype.addMedia = function(data, user) { m.temp = data.temp; var res = self.playlist.addMedia(m); if (res.error) { + console.log(res); user.socket.emit("queueFail", res.error); q.release(); return; @@ -1414,6 +1419,36 @@ Channel.prototype.addMedia = function(data, user) { q.release(); }; + // special case for youtube playlists + if (data.type === "yp") { + self.plqueue.queue(function (q) { + if (self.dead) + return; + self.server.infogetter.getMedia(data.id, data.type, + function (e, vids) { + if (e) { + user.socket.emit("queueFail", e); + q.release(); + return; + } + + if (data.pos === "next") { + vids.reverse(); + if (self.playlist.length === 0) + vids.unshift(vids.pop()); + } + + var fake = { release: function () { } }; + var cb = afterData.bind(self, fake, false); + for (var i = 0; i < vids.length; i++) { + cb(vids[i]); + } + q.release(); + }); + }); + return; + } + // Don't check library for livestreams or if the channel is // unregistered if (!self.registered || isLive(data.type)) { @@ -1476,48 +1511,20 @@ Channel.prototype.addMedia = function(data, user) { } }); } -} - -Channel.prototype.addMediaList = function(data, user) { - var chan = this; - this.playlist.addMediaList(data, function(err, item) { - if (chan.dead) - return; - - if(err) { - if(err === true) - err = false; - if(user) - user.socket.emit("queueFail", err); - return; - } - else { - chan.logger.log("### " + user.name + " queued " + item.media.title); - item.temp = data.temp; - item.queueby = data.queueby; - chan.sendAll("queue", { - item: item.pack(), - after: item.prev ? item.prev.uid : "prepend" - }); - chan.broadcastPlaylistMeta(); - if(!item.temp) - chan.cacheMedia(item.media); - } - }); -} +}; Channel.prototype.tryQueuePlaylist = function(user, data) { var self = this; - if(!this.hasPermission(user, "playlistaddlist")) { + if (!self.hasPermission(user, "playlistaddlist")) { return; } - if(typeof data.name != "string" || - typeof data.pos != "string") { + if(typeof data.name !== "string" || + typeof data.pos !== "string") { return; } - if(data.pos == "next" && !this.hasPermission(user, "playlistnext")) { + if (data.pos == "next" && !this.hasPermission(user, "playlistnext")) { return; } @@ -1526,16 +1533,24 @@ Channel.prototype.tryQueuePlaylist = function(user, data) { if (self.dead) return; - if(err) { + if (err) { user.socket.emit("errorMsg", { msg: "Playlist load failed: " + err }); return; } - data.list = pl; - data.queueby = user.name; - data.temp = !self.hasPermission(user, "addnontemp"); - self.addMediaList(data, user); + + if (data.pos === "next") { + pl.reverse(); + if (self.playlist.items.length === 0) + pl.unshift(pl.pop()); + } + + for (var i = 0; i < pl.length; i++) { + pl[i].pos = data.pos; + pl[i].temp = !self.hasPermission(user, "addnontemp"); + self.addMedia(pl[i], user); + } }); } diff --git a/lib/playlist.js b/lib/playlist.js index 337d3730..0a4bf4ae 100644 --- a/lib/playlist.js +++ b/lib/playlist.js @@ -230,7 +230,7 @@ Playlist.prototype.addMedia = function (data) { if (this.current !== null) pos = this.current.uid; else - pos = "append" + pos = "append"; } var m = new Media(data.id, data.title, data.seconds, data.type); @@ -239,177 +239,10 @@ Playlist.prototype.addMedia = function (data) { item.temp = data.temp; return { item: item, - error: this.add(item, data.pos) + error: this.add(item, pos) }; }; -Playlist.prototype.addCachedMedia = function(data, callback) { - var pos = "append"; - if(data.pos == "next") { - if(!this.current) - pos = "prepend"; - else - pos = this.current.uid; - } - - var it = this.makeItem(data.media); - it.temp = data.temp; - it.queueby = data.queueby; - - var pl = this; - - var action = { - fn: function() { - var err = pl.add(it, pos); - callback(err, err ? null : it); - }, - waiting: false - }; - this.queueAction(action); -} - -Playlist.prototype.__addMedia = function (data, cb) { - var self = this; - if (data.type === "yp") { - this.addYouTubePlaylist(data, cb); - return; - } - - this.fnqueue.queue(function (q) { - var afterData = function (m) { - if (data.maxlength && data.seconds > m.maxlength) { - setImmediate(function () { - cb("Media is too long!", null); - }); - q.release(); - return; - } - - var it = self.makeItem(m); - it.queueby = data.queueby; - it.temp = data.temp; - var pos = "append"; - if(data.pos == "next") { - if (!self.current) - pos = "prepend"; - else - pos = self.current.uid; - } - var err = self.add(it, pos); - setImmediate(function () { - cb(err, err ? null : it); - }); - q.release(); - }; - - if (typeof data.title === "string") { - afterData(new Media(data.id, data.title, data.seconds, data.type)); - } else { - self.server.infogetter.getMedia(data.id, data.type, - function (e, m) { - if (e) { - setImmediate(function () { cb(e, null); }); - q.release(); - return; - } - - afterData(m); - }); - } - }); -}; - -Playlist.prototype._addMedia = function(data, callback) { - - if(data.type == "yp") { - this.addYouTubePlaylist(data, callback); - return; - } - - var pos = "append"; - if(data.pos == "next") { - if(!this.current) - pos = "prepend"; - else - pos = this.current.uid; - } - - var it = this.makeItem(null); - var pl = this; - var action = { - fn: function() { - var err = pl.add(it, pos); - callback(err, err ? null : it); - }, - waiting: true - }; - this.queueAction(action); - - // Pre-cached data - if(typeof data.title === "string" && - typeof data.seconds === "number") { - if(data.maxlength && data.seconds > data.maxlength) { - action.expire = 0; - callback("Media is too long!", null); - return; - } - it.media = new Media(data.id, data.title, data.seconds, data.type); - action.waiting = false; - return; - } - - this.server.infogetter.getMedia(data.id, data.type, function(err, media) { - if(err) { - action.expire = 0; - callback(err, null); - return; - } - - if(data.maxlength && media.seconds > data.maxlength) { - action.expire = 0; - callback("Media is too long!", null); - return; - } - - it.media = media; - it.temp = data.temp; - it.queueby = data.queueby; - action.waiting = false; - }); -} - -Playlist.prototype.addMediaList = function(data, callback) { - var start = false; - if(data.pos == "next") { - data.list = data.list.reverse(); - start = data.list[data.list.length - 1]; - } - - if(this.items.length != 0) - start = false; - - var pl = this; - for(var i = 0; i < data.list.length; i++) { - var x = data.list[i]; - x.pos = data.pos; - if(start && x == start) { - pl.addMedia(x, function (err, item) { - if(err) { - callback(err, item); - } - else { - callback(err, item); - pl.current = item; - pl.startPlayback(); - } - }); - } - else { - pl.addMedia(x, callback); - } - } -} - Playlist.prototype.addYouTubePlaylist = function(data, callback) { var pos = "append"; if(data.pos == "next") { From 0eda0b8ed2f33d3fd45f04cf3776dd6109c2b5a4 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Mon, 30 Sep 2013 09:51:38 -0500 Subject: [PATCH 05/14] Fix comma separated queues --- lib/channel.js | 5 +++++ www/assets/js/ui.js | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/channel.js b/lib/channel.js index 1f3da550..ea3dbe1d 100644 --- a/lib/channel.js +++ b/lib/channel.js @@ -1343,6 +1343,11 @@ Channel.prototype.tryQueue = function(user, data) { data.temp = !this.hasPermission(user, "addnontemp"); if (data.list) { + if (data.pos === "next") { + data.list.reverse(); + if (this.playlist.items.length === 0) + data.list.unshift(data.list.pop()); + } for (var i = 0; i < data.list.length; i++) { data.list[i].pos = data.pos; this.tryQueue(user, data.list[i]); diff --git a/www/assets/js/ui.js b/www/assets/js/ui.js index 997d5eb9..14b977fb 100644 --- a/www/assets/js/ui.js +++ b/www/assets/js/ui.js @@ -323,9 +323,6 @@ function queue(pos) { return; } var links = $("#mediaurl").val().split(","); - if(pos == "next") { - links = links.reverse(); - } var parsed = []; links.forEach(function(link) { var data = parseMediaLink(link); From 3666b43f7aee54dc6b28421bbfc86293b3f2da57 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Mon, 30 Sep 2013 10:05:09 -0500 Subject: [PATCH 06/14] Use new asyncqueue system for moving and deleting --- lib/channel.js | 69 ++++++++++++++---------- lib/playlist.js | 141 +++++++++++------------------------------------- 2 files changed, 70 insertions(+), 140 deletions(-) diff --git a/lib/channel.js b/lib/channel.js index ea3dbe1d..8b40290b 100644 --- a/lib/channel.js +++ b/lib/channel.js @@ -1587,15 +1587,20 @@ Channel.prototype.trySetTemp = function(user, data) { Channel.prototype.dequeue = function(uid) { - var chan = this; - function afterDelete() { - chan.sendAll("delete", { - uid: uid - }); - chan.broadcastPlaylistMeta(); - } - if(!this.playlist.remove(uid, afterDelete)) - return; + var self = this; + self.plqueue.queue(function (q) { + if (self.dead) + return; + + if (self.playlist.remove(uid)) { + self.sendAll("delete", { + uid: uid + }); + self.broadcastPlaylistMeta(); + } + + q.release(); + }); } Channel.prototype.tryDequeue = function(user, data) { @@ -1666,6 +1671,7 @@ Channel.prototype.tryJumpTo = function(user, data) { Channel.prototype.clearqueue = function() { this.playlist.clear(); + this.plqueue.reset(); this.sendAll("playlist", this.playlist.items.toArray()); this.broadcastPlaylistMeta(); } @@ -1683,6 +1689,7 @@ Channel.prototype.shufflequeue = function() { var n = []; var pl = this.playlist.items.toArray(false); this.playlist.clear(); + this.plqueue.reset(); while(pl.length > 0) { var i = parseInt(Math.random() * pl.length); var item = this.playlist.makeItem(pl[i].media); @@ -1732,32 +1739,36 @@ Channel.prototype.tryUpdate = function(user, data) { } Channel.prototype.move = function(data, user) { - var chan = this; - function afterMove() { - if (chan.dead) + var self = this; + self.plqueue.queue(function (q) { + if (self.dead) return; - var moveby = user && user.name ? user.name : null; - if(typeof data.moveby !== "undefined") - moveby = data.moveby; + if (self.playlist.move(data.from, data.after)) { + var moveby = user && user.name ? user.name : null; + if (typeof data.moveby !== "undefined") + moveby = data.moveby; - var fromit = chan.playlist.items.find(data.from); - var afterit = chan.playlist.items.find(data.after); - var aftertitle = afterit && afterit.media ? afterit.media.title : ""; - if(fromit) { - chan.logger.log("### " + user.name + " moved " + fromit.media.title - + (aftertitle ? " after " + aftertitle : "")); + var fromit = self.playlist.items.find(data.from); + var afterit = self.playlist.items.find(data.after); + var aftertitle = (afterit && afterit.media) + ? afterit.media.title : ""; + if (fromit) { + self.logger.log("### " + user.name + " moved " + + fromit.media.title + + (aftertitle ? " after " + aftertitle : "")); + } + + self.sendAll("moveVideo", { + from: data.from, + after: data.after, + moveby: moveby + }); } - chan.sendAll("moveVideo", { - from: data.from, - after: data.after, - moveby: moveby - }); - } - - this.playlist.move(data.from, data.after, afterMove); + q.release(); + }); } Channel.prototype.tryMove = function(user, data) { diff --git a/lib/playlist.js b/lib/playlist.js index 0a4bf4ae..765d5ab8 100644 --- a/lib/playlist.js +++ b/lib/playlist.js @@ -51,10 +51,7 @@ function Playlist(chan) { "mediaUpdate": [], "remove": [], }; - this.lock = false; - this.action_queue = []; this.fnqueue = new AsyncQueue(); - this._qaInterval = false; AllPlaylists[name] = this; this.channel = chan; @@ -88,28 +85,6 @@ function Playlist(chan) { }); } -Playlist.prototype.queueAction = function(data) { - this.action_queue.push(data); - if(this._qaInterval) - return; - var pl = this; - this._qaInterval = setInterval(function() { - var data = pl.action_queue.shift(); - if(data.waiting) { - if(!("expire" in data)) - data.expire = Date.now() + 10000; - if(Date.now() < data.expire) - pl.action_queue.unshift(data); - } - else - data.fn(); - if(pl.action_queue.length == 0) { - clearInterval(pl._qaInterval); - pl._qaInterval = false; - } - }, 100); -} - Playlist.prototype.dump = function() { var arr = this.items.toArray(); var pos = 0; @@ -243,88 +218,35 @@ Playlist.prototype.addMedia = function (data) { }; }; -Playlist.prototype.addYouTubePlaylist = function(data, callback) { - var pos = "append"; - if(data.pos == "next") { - if(!this.current) - pos = "prepend"; - else - pos = this.current.uid; - } - - var pl = this; - this.server.infogetter.getMedia(data.id, data.type, function(err, vids) { - if(err) { - callback(err, null); - return; +Playlist.prototype.remove = function (uid) { + var self = this; + var item = self.items.find(uid); + if (item && self.items.remove(uid)) { + if (item === self.current) { + setImmediate(function () { self._next(); }); } - - if(data.pos === "next") - vids.reverse(); - - vids.forEach(function(media) { - if(data.maxlength && media.seconds > data.maxlength) { - callback("Media is too long!", null); - return; - } - var it = pl.makeItem(media); - it.temp = data.temp; - it.queueby = data.queueby; - pl.queueAction({ - fn: function() { - var err = pl.add(it, pos); - callback(err, err ? null : it); - }, - }); - }); - }); + return true; + } else { + return false; + } } -Playlist.prototype.remove = function(uid, callback) { - var pl = this; - this.queueAction({ - fn: function() { - var item = pl.items.find(uid); - if(pl.items.remove(uid)) { - if(callback) - callback(); - if(item == pl.current) - pl._next(); - } - }, - waiting: false - }); -} - -Playlist.prototype.move = function(from, after, callback) { - var pl = this; - this.queueAction({ - fn: function() { - pl._move(from, after, callback); - }, - waiting: false - }); -} - -Playlist.prototype._move = function(from, after, callback) { +Playlist.prototype.move = function (from, after) { var it = this.items.find(from); - if(!this.items.remove(from)) - return; + if (!this.items.remove(from)) + return false; - if(after === "prepend") { - if(!this.items.prepend(it)) - return; + if (after === "prepend") { + if (!this.items.prepend(it)) + return false; + } else if (after === "append") { + if (!this.items.append(it)) + return false; + } else if (!this.items.insertAfter(it, after)) { + return false; } - else if(after === "append") { - if(!this.items.append(it)) - return; - } - - else if(!this.items.insertAfter(it, after)) - return; - - callback(); + return true; } Playlist.prototype.next = function() { @@ -333,13 +255,11 @@ Playlist.prototype.next = function() { var it = this.current; - if(it.temp) { - var pl = this; - this.remove(it.uid, function() { - pl.on("remove")(it); - }); - } - else { + if (it.temp) { + if (this.remove(it.uid)) { + this.on("remove")(it); + } + } else { this._next(); } @@ -375,10 +295,9 @@ Playlist.prototype.jump = function(uid) { } if(it.temp) { - var pl = this; - this.remove(it.uid, function () { - pl.on("remove")(it); - }); + if (this.remove(it.uid)) { + this.on("remove")(it); + } } return this.current; From 0c52c3f17d0756d0f4bf39fba3407c92e62579c9 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Mon, 30 Sep 2013 21:34:40 -0500 Subject: [PATCH 07/14] Fix /clean --- lib/playlist.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/playlist.js b/lib/playlist.js index 765d5ab8..e8ff7772 100644 --- a/lib/playlist.js +++ b/lib/playlist.js @@ -423,10 +423,12 @@ Playlist.prototype.clean = function (filter) { if (count < matches.length) { var uid = matches[count].uid; count++; - self.channel.sendAll("delete", { - uid: uid - }); - self.remove(uid, deleteNext); + if (self.remove(uid)) { + self.channel.sendAll("delete", { + uid: uid + }); + } + deleteNext(); } else { // refresh meta only once, at the end self.channel.broadcastPlaylistMeta(); From 54016f6f48cf7735b90b3980388755d0d651a7bb Mon Sep 17 00:00:00 2001 From: calzoneman Date: Mon, 30 Sep 2013 21:54:01 -0500 Subject: [PATCH 08/14] Remove debug message; stack queueFail messages clientside --- lib/channel.js | 1 - www/assets/js/callbacks.js | 23 +++++++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/channel.js b/lib/channel.js index 8b40290b..2cf4fda3 100644 --- a/lib/channel.js +++ b/lib/channel.js @@ -1405,7 +1405,6 @@ Channel.prototype.addMedia = function(data, user) { m.temp = data.temp; var res = self.playlist.addMedia(m); if (res.error) { - console.log(res); user.socket.emit("queueFail", res.error); q.release(); return; diff --git a/www/assets/js/callbacks.js b/www/assets/js/callbacks.js index 3d45cb6d..f342ed24 100644 --- a/www/assets/js/callbacks.js +++ b/www/assets/js/callbacks.js @@ -796,11 +796,30 @@ Callbacks = { }, queueFail: function(data) { - if(!data) { + if (!data) { data = "Queue failed. Check your link to make sure it is valid."; } + var alerts = $(".qfalert"); + for (var i = 0; i < alerts.length; i++) { + var al = $(alerts[i]); + var cl = al.clone(); + cl.children().remove(); + if (cl.text() === data) { + var tag = al.find(".label-important"); + if (tag.length > 0) { + var count = parseInt(tag.text().match(/\d+/)[0]) + 1; + tag.text(tag.text().replace(/\d+/, ""+count)); + } else { + $("") + .addClass("label label-important pull-right") + .text("+ 1 more") + .appendTo(al); + } + return; + } + } makeAlert("Error", data, "alert-error") - .addClass("span12") + .addClass("span12 qfalert") .insertBefore($("#extended_controls")); }, From 54dee9e25da5d42d18b5f4a045f2dfac9305bc26 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Mon, 30 Sep 2013 21:59:04 -0500 Subject: [PATCH 09/14] Fix some error messages, intialize announcement to null --- lib/get-info.js | 5 ++++- lib/server.js | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/get-info.js b/lib/get-info.js index 3bbe2ad9..c263f595 100644 --- a/lib/get-info.js +++ b/lib/get-info.js @@ -82,11 +82,14 @@ module.exports = function (Server) { } else if(status === 403) { callback("Private video", null); return; + } else if(status === 400) { + callback("Invalid video", null); + return; } else if(status === 503) { callback("API failure", null); return; } else if(status !== 200) { - callback(true, null); + callback("HTTP " + status, null); return; } diff --git a/lib/server.js b/lib/server.js index 012a98e3..9dcd396a 100644 --- a/lib/server.js +++ b/lib/server.js @@ -79,6 +79,7 @@ var Server = { db: null, ips: {}, acp: null, + announcement: null, httpaccess: null, actionlog: null, logHTTP: function (req, status) { From 29a1f0b9d3d963ae1821c224184f5dc8994fc132 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Tue, 1 Oct 2013 13:14:44 -0500 Subject: [PATCH 10/14] Throttle comma-separated lists properly --- lib/channel.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/channel.js b/lib/channel.js index 2cf4fda3..ed1744c8 100644 --- a/lib/channel.js +++ b/lib/channel.js @@ -1348,10 +1348,19 @@ Channel.prototype.tryQueue = function(user, data) { if (this.playlist.items.length === 0) data.list.unshift(data.list.pop()); } - for (var i = 0; i < data.list.length; i++) { - data.list[i].pos = data.pos; - this.tryQueue(user, data.list[i]); - } + var i = 0; + var self = this; + var next = function () { + if (self.dead) + return; + if (i < data.list.length) { + data.list[i].pos = data.pos; + self.tryQueue(user, data.list[i]); + i++; + setTimeout(next, 2000); + } + }; + next(); } else { this.addMedia(data, user); } From acb5136c151db0a7c051eb4d7f9c4097c8c88943 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Tue, 1 Oct 2013 13:25:05 -0500 Subject: [PATCH 11/14] Improve setCurrent --- www/assets/js/callbacks.js | 43 +++++++++++++++----------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/www/assets/js/callbacks.js b/www/assets/js/callbacks.js index f342ed24..840749a7 100644 --- a/www/assets/js/callbacks.js +++ b/www/assets/js/callbacks.js @@ -767,24 +767,24 @@ Callbacks = { queueAction({ fn: function () { var li = makeQueueEntry(data.item, true); + if (data.item.uid === PL_CURRENT) + li.addClass("queue_active"); li.hide(); var q = $("#queue"); li.attr("title", data.item.queueby ? ("Added by: " + data.item.queueby) : "Added by: Unknown"); - if(data.after === "prepend") { + if (data.after === "prepend") { li.prependTo(q); li.show("blind"); return true; - } - else if(data.after === "append") { + } else if (data.after === "append") { li.appendTo(q); li.show("blind"); return true; - } - else { + } else { var liafter = playlistFind(data.after); - if(!liafter) { + if (!liafter) { return false; } li.insertAfter(liafter); @@ -873,27 +873,18 @@ Callbacks = { }, setCurrent: function(uid) { - queueAction({ - fn: function () { - PL_CURRENT = uid; - var qli = $("#queue li"); - qli.removeClass("queue_active"); - var li = $(".pluid-" + uid); - if(li.length == 0) { - return false; + PL_CURRENT = uid; + $("#queue li").removeClass("queue_active"); + var li = $(".pluid-" + uid); + if (li.length !== 0) { + li.addClass("queue_active"); + var tmr = setInterval(function () { + if (!PL_WAIT_SCROLL) { + scrollQueue(); + clearInterval(tmr); } - - li.addClass("queue_active"); - var timer = setInterval(function () { - if(!PL_WAIT_SCROLL) { - scrollQueue(); - clearInterval(timer); - } - }, 100); - return true; - }, - can_wait: true - }); + }, 100); + } }, changeMedia: function(data) { From a1c72aaa8d567ba702b15571b8e57d9edb2f79be Mon Sep 17 00:00:00 2001 From: calzoneman Date: Tue, 1 Oct 2013 13:35:29 -0500 Subject: [PATCH 12/14] Use asyncqueue clientside too --- lib/asyncqueue.js | 11 +++++ www/assets/js/callbacks.js | 83 +++++++++++++++++----------------- www/assets/js/data.js | 5 +-- www/assets/js/util.js | 91 ++++++++++++++++++++++++++------------ 4 files changed, 115 insertions(+), 75 deletions(-) diff --git a/lib/asyncqueue.js b/lib/asyncqueue.js index 2383056c..7d450855 100644 --- a/lib/asyncqueue.js +++ b/lib/asyncqueue.js @@ -1,3 +1,14 @@ +/* +The MIT License (MIT) +Copyright (c) 2013 Calvin Montgomery + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + var AsyncQueue = function () { this._q = []; this._lock = false; diff --git a/www/assets/js/callbacks.js b/www/assets/js/callbacks.js index 840749a7..a5c5bc81 100644 --- a/www/assets/js/callbacks.js +++ b/www/assets/js/callbacks.js @@ -764,33 +764,35 @@ Callbacks = { }, queue: function(data) { - queueAction({ - fn: function () { - var li = makeQueueEntry(data.item, true); - if (data.item.uid === PL_CURRENT) - li.addClass("queue_active"); - li.hide(); - var q = $("#queue"); - li.attr("title", data.item.queueby - ? ("Added by: " + data.item.queueby) - : "Added by: Unknown"); - if (data.after === "prepend") { - li.prependTo(q); - li.show("blind"); - return true; - } else if (data.after === "append") { - li.appendTo(q); - li.show("blind"); - return true; - } else { - var liafter = playlistFind(data.after); - if (!liafter) { - return false; - } - li.insertAfter(liafter); - li.show("blind"); - return true; + PL_ACTION_QUEUE.queue(function (plq) { + var li = makeQueueEntry(data.item, true); + if (data.item.uid === PL_CURRENT) + li.addClass("queue_active"); + li.hide(); + var q = $("#queue"); + li.attr("title", data.item.queueby + ? ("Added by: " + data.item.queueby) + : "Added by: Unknown"); + if (data.after === "prepend") { + li.prependTo(q); + li.show("blind", function () { + plq.release(); + }); + } else if (data.after === "append") { + li.appendTo(q); + li.show("blind", function () { + plq.release(); + }); + } else { + var liafter = playlistFind(data.after); + if (!liafter) { + plq.release(); + return; } + li.insertAfter(liafter); + li.show("blind", function () { + plq.release(); + }); } }); }, @@ -848,26 +850,23 @@ Callbacks = { }, "delete": function(data) { - queueAction({ - fn: function () { - PL_WAIT_SCROLL = true; - var li = $(".pluid-" + data.uid); - li.hide("blind", function() { - li.remove(); - PL_WAIT_SCROLL = false; - }); - return true; - } + PL_ACTION_QUEUE.queue(function (plq) { + PL_WAIT_SCROLL = true; + var li = $(".pluid-" + data.uid); + li.hide("blind", function() { + li.remove(); + plq.release(); + PL_WAIT_SCROLL = false; + }); }); }, moveVideo: function(data) { - if(data.moveby != CLIENT.name) { - queueAction({ - fn: function () { - playlistMove(data.from, data.after); - return true; - } + if (data.moveby != CLIENT.name) { + PL_ACTION_QUEUE.queue(function (plq) { + playlistMove(data.from, data.after, function () { + plq.release(); + }); }); } }, diff --git a/www/assets/js/data.js b/www/assets/js/data.js index 7d3b6518..be892386 100644 --- a/www/assets/js/data.js +++ b/www/assets/js/data.js @@ -9,7 +9,7 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -var CL_VERSION = "2.0.0"; +var CL_VERSION = "2.4.3"; var CLIENT = { rank: -1, @@ -44,9 +44,6 @@ if($("#videowidth").length > 0) { VWIDTH = $("#videowidth").css("width").replace("px", ""); VHEIGHT = ""+parseInt(parseInt(VWIDTH) * 9 / 16); } -var PL_MOVING = false; -var PL_ADDING = false; -var PL_DELETING = false; var REBUILDING = false; var socket = { emit: function() { diff --git a/www/assets/js/util.js b/www/assets/js/util.js index f2a21ef6..d5933273 100644 --- a/www/assets/js/util.js +++ b/www/assets/js/util.js @@ -1191,29 +1191,58 @@ function addLibraryButtons(li, id, source) { /* queue stuff */ -var PL_QUEUED_ACTIONS = []; -var PL_ACTION_INTERVAL = false; +var AsyncQueue = function () { + this._q = []; + this._lock = false; + this._tm = 0; +}; -function queueAction(data) { - PL_QUEUED_ACTIONS.push(data); - if(PL_ACTION_INTERVAL) - return; - PL_ACTION_INTERVAL = setInterval(function () { - var data = PL_QUEUED_ACTIONS.shift(); - if(!("expire" in data)) - data.expire = Date.now() + 5000; - if(!data.fn()) { - if(data.can_wait && Date.now() < data.expire) - PL_QUEUED_ACTIONS.push(data); - else if(Date.now() < data.expire) - PL_QUEUED_ACTIONS.unshift(data); +AsyncQueue.prototype.next = function () { + if (this._q.length > 0) { + if (!this.lock()) + return; + var item = this._q.shift(); + var fn = item[0], tm = item[1]; + this._tm = Date.now() + item[1]; + fn(this); + } +}; + +AsyncQueue.prototype.lock = function () { + if (this._lock) { + if (this._tm > 0 && Date.now() > this._tm) { + this._tm = 0; + return true; } - if(PL_QUEUED_ACTIONS.length == 0) { - clearInterval(PL_ACTION_INTERVAL); - PL_ACTION_INTERVAL = false; - } - }, 100); -} + return false; + } + + this._lock = true; + return true; +}; + +AsyncQueue.prototype.release = function () { + var self = this; + if (!self._lock) + return false; + + self._lock = false; + self.next(); + return true; +}; + +AsyncQueue.prototype.queue = function (fn) { + var self = this; + self._q.push([fn, 20000]); + self.next(); +}; + +AsyncQueue.prototype.reset = function () { + this._q = []; + this._lock = false; +}; + +var PL_ACTION_QUEUE = new AsyncQueue(); // Because jQuery UI does weird things function playlistFind(uid) { @@ -1227,10 +1256,12 @@ function playlistFind(uid) { return false; } -function playlistMove(from, after) { +function playlistMove(from, after, cb) { var lifrom = $(".pluid-" + from); - if(lifrom.length == 0) - return false; + if(lifrom.length == 0) { + cb(false); + return; + } var q = $("#queue"); @@ -1238,24 +1269,26 @@ function playlistMove(from, after) { lifrom.hide("blind", function() { lifrom.detach(); lifrom.prependTo(q); - lifrom.show("blind"); + lifrom.show("blind", cb); }); } else if(after === "append") { lifrom.hide("blind", function() { lifrom.detach(); lifrom.appendTo(q); - lifrom.show("blind"); + lifrom.show("blind", cb); }); } else { var liafter = $(".pluid-" + after); - if(liafter.length == 0) - return false; + if(liafter.length == 0) { + cb(false); + return; + } lifrom.hide("blind", function() { lifrom.detach(); lifrom.insertAfter(liafter); - lifrom.show("blind"); + lifrom.show("blind", cb); }); } } From 1e2a1425247197569da9c8e824068ddd3751c1f3 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Tue, 1 Oct 2013 14:26:32 -0500 Subject: [PATCH 13/14] Fix 'error: true' --- www/assets/js/callbacks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/assets/js/callbacks.js b/www/assets/js/callbacks.js index a5c5bc81..4149d5ba 100644 --- a/www/assets/js/callbacks.js +++ b/www/assets/js/callbacks.js @@ -798,7 +798,7 @@ Callbacks = { }, queueFail: function(data) { - if (!data) { + if (!data || data === true) { data = "Queue failed. Check your link to make sure it is valid."; } var alerts = $(".qfalert"); From a23a55f6a2f9092ba2b8566a5bcefc3283df6cf4 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Tue, 1 Oct 2013 23:02:31 -0500 Subject: [PATCH 14/14] Update changelog and version # --- changelog | 14 ++++++++++++++ lib/server.js | 2 +- package.json | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/changelog b/changelog index 7e1d3292..21804177 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,17 @@ +Tue Oct 01 22:57 2013 CDT + * lib/asyncqueue.js: Add a generalized queue class for queueing async + functions to execute in order + * lib/channel.js, lib/playlist.js: Clean up playlist addition/move/ + deletion. Should fix #285. + * lib/server.js: Fix announcement initialization to null + * tests/naokosimulator2013.js: Add a simple bot that authenticates + and vomits a giant list of videos into a channel + * www/assets/js/callbacks.js, www/assets/js/util.js: Clean up + playlist add/move/delete + * www/assets/js/data.js: Update CL_VERSION which I always forget + to do. + * www/assets/js/ui.js: Small fix to comply with server changes + Fri Sep 27 10:27 2013 CDT * lib/channel.js: Change the link regex, change the way 'affects links' works. The default link filter will only transform a link of no diff --git a/lib/server.js b/lib/server.js index 9dcd396a..e010215e 100644 --- a/lib/server.js +++ b/lib/server.js @@ -8,7 +8,7 @@ var Logger = require("./logger"); var Channel = require("./channel"); var User = require("./user"); -const VERSION = "2.4.2"; +const VERSION = "2.4.3"; function getIP(req) { var raw = req.connection.remoteAddress; diff --git a/package.json b/package.json index 89e4d2db..50e7316a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Calvin Montgomery", "name": "CyTube", "description": "Online media synchronizer and chat", - "version": "2.4.2", + "version": "2.4.3", "repository": { "url": "http://github.com/calzoneman/sync" },