mirror of https://github.com/calzoneman/sync.git
Merge
This commit is contained in:
commit
e6234297a1
10
NEWS.md
10
NEWS.md
|
@ -1,3 +1,13 @@
|
||||||
|
2016-01-06
|
||||||
|
==========
|
||||||
|
|
||||||
|
This release updates socket.io to version 1.4.0. The updates to socket.io
|
||||||
|
include a few security-related fixes, so please be sure to run `npm install`
|
||||||
|
to ensure the updated version is installed before restarting your CyTube server.
|
||||||
|
|
||||||
|
* https://nodesecurity.io/advisories/67
|
||||||
|
* https://github.com/socketio/engine.io/commit/391ce0dc8b88a6609d88db83ea064040a05ab803
|
||||||
|
|
||||||
2015-10-25
|
2015-10-25
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,11 @@ io:
|
||||||
default-port: 1337
|
default-port: 1337
|
||||||
# limit the number of concurrent socket connections per IP address
|
# limit the number of concurrent socket connections per IP address
|
||||||
ip-connection-limit: 10
|
ip-connection-limit: 10
|
||||||
|
# Whether or not to use zlib to compress each socket message (this option is
|
||||||
|
# passed through to socket.io/engine.io).
|
||||||
|
# Note that while this may save a little bandwidth, it also consumes a lot
|
||||||
|
# more CPU and will bottleneck pretty quickly under heavy load.
|
||||||
|
per-message-deflate: false
|
||||||
|
|
||||||
# Mailer details (used for sending password reset links)
|
# Mailer details (used for sending password reset links)
|
||||||
# see https://github.com/andris9/Nodemailer
|
# see https://github.com/andris9/Nodemailer
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"author": "Calvin Montgomery",
|
"author": "Calvin Montgomery",
|
||||||
"name": "CyTube",
|
"name": "CyTube",
|
||||||
"description": "Online media synchronizer and chat",
|
"description": "Online media synchronizer and chat",
|
||||||
"version": "3.12.1",
|
"version": "3.14.3",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "http://github.com/calzoneman/sync"
|
"url": "http://github.com/calzoneman/sync"
|
||||||
},
|
},
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
"create-error": "^0.3.1",
|
"create-error": "^0.3.1",
|
||||||
"csrf": "^3.0.0",
|
"csrf": "^3.0.0",
|
||||||
"cytube-mediaquery": "git://github.com/CyTube/mediaquery",
|
"cytube-mediaquery": "git://github.com/CyTube/mediaquery",
|
||||||
"cytubefilters": "git://github.com/calzoneman/cytubefilters#095b7956",
|
"cytubefilters": "git://github.com/calzoneman/cytubefilters#67c7c69a",
|
||||||
"express": "^4.13.3",
|
"express": "^4.13.3",
|
||||||
"express-minify": "^0.1.6",
|
"express-minify": "^0.1.6",
|
||||||
"graceful-fs": "^4.1.2",
|
"graceful-fs": "^4.1.2",
|
||||||
|
@ -34,9 +34,9 @@
|
||||||
"redis": "^2.4.2",
|
"redis": "^2.4.2",
|
||||||
"sanitize-html": "git://github.com/calzoneman/sanitize-html",
|
"sanitize-html": "git://github.com/calzoneman/sanitize-html",
|
||||||
"serve-static": "^1.10.0",
|
"serve-static": "^1.10.0",
|
||||||
"socket.io": "^1.3.7",
|
"socket.io": "^1.4.0",
|
||||||
"socket.io-redis": "^1.0.0",
|
"socket.io-redis": "^1.0.0",
|
||||||
"source-map-support": "^0.3.2",
|
"source-map-support": "^0.4.0",
|
||||||
"status-message-polyfill": "calzoneman/status-message-polyfill",
|
"status-message-polyfill": "calzoneman/status-message-polyfill",
|
||||||
"uuid": "^2.0.1",
|
"uuid": "^2.0.1",
|
||||||
"yamljs": "^0.1.6"
|
"yamljs": "^0.1.6"
|
||||||
|
|
|
@ -5,7 +5,7 @@ codecToMimeType = (codec) ->
|
||||||
when 'matroska/vp8', 'matroska/vp9' then 'video/webm'
|
when 'matroska/vp8', 'matroska/vp9' then 'video/webm'
|
||||||
when 'ogg/theora' then 'video/ogg'
|
when 'ogg/theora' then 'video/ogg'
|
||||||
when 'mp3' then 'audio/mp3'
|
when 'mp3' then 'audio/mp3'
|
||||||
when 'vorbis' then 'audio/vorbis'
|
when 'vorbis' then 'audio/ogg'
|
||||||
else 'video/flv'
|
else 'video/flv'
|
||||||
|
|
||||||
window.FilePlayer = class FilePlayer extends VideoJSPlayer
|
window.FilePlayer = class FilePlayer extends VideoJSPlayer
|
||||||
|
|
|
@ -12,39 +12,65 @@ import * as ChannelStore from '../channel-storage/channelstore';
|
||||||
import { ChannelStateSizeError } from '../errors';
|
import { ChannelStateSizeError } from '../errors';
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
|
|
||||||
/**
|
class ReferenceCounter {
|
||||||
* Previously, async channel functions were riddled with race conditions due to
|
constructor(channel) {
|
||||||
* an event causing the channel to be unloaded while a pending callback still
|
|
||||||
* needed to reference it.
|
|
||||||
*
|
|
||||||
* This solution should be better than constantly checking whether the channel
|
|
||||||
* has been unloaded in nested callbacks. The channel won't be unloaded until
|
|
||||||
* nothing needs it anymore. Conceptually similar to a reference count.
|
|
||||||
*/
|
|
||||||
function ActiveLock(channel) {
|
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
this.count = 0;
|
this.channelName = channel.name;
|
||||||
}
|
this.refCount = 0;
|
||||||
|
this.references = {};
|
||||||
|
}
|
||||||
|
|
||||||
ActiveLock.prototype = {
|
ref(caller) {
|
||||||
lock: function () {
|
if (caller) {
|
||||||
this.count++;
|
if (this.references.hasOwnProperty(caller)) {
|
||||||
},
|
this.references[caller]++;
|
||||||
|
} else {
|
||||||
|
this.references[caller] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
release: function () {
|
this.refCount++;
|
||||||
this.count--;
|
}
|
||||||
if (this.count === 0) {
|
|
||||||
/* sanity check */
|
unref(caller) {
|
||||||
if (this.channel.users.length > 0) {
|
if (caller) {
|
||||||
Logger.errlog.log("Warning: ActiveLock count=0 but users.length > 0 (" +
|
if (this.references.hasOwnProperty(caller)) {
|
||||||
"channel: " + this.channel.name + ")");
|
this.references[caller]--;
|
||||||
this.count = this.channel.users.length;
|
if (this.references[caller] === 0) {
|
||||||
|
delete this.references[caller];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger.errlog.log("ReferenceCounter::unref() called by caller [" +
|
||||||
|
caller + "] but this caller had no active references! " +
|
||||||
|
`(channel: ${this.channelName})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.refCount--;
|
||||||
|
this.checkRefCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
checkRefCount() {
|
||||||
|
if (this.refCount === 0) {
|
||||||
|
if (Object.keys(this.references).length > 0) {
|
||||||
|
Logger.errlog.log("ReferenceCounter::refCount reached 0 but still had " +
|
||||||
|
"active references: " +
|
||||||
|
JSON.stringify(Object.keys(this.references)) +
|
||||||
|
` (channel: ${this.channelName})`);
|
||||||
|
for (var caller in this.references) {
|
||||||
|
this.refCount += this.references[caller];
|
||||||
|
}
|
||||||
|
} else if (this.channel.users.length > 0) {
|
||||||
|
Logger.errlog.log("ReferenceCounter::refCount reached 0 but still had " +
|
||||||
|
this.channel.users.length + " active users" +
|
||||||
|
` (channel: ${this.channelName})`);
|
||||||
|
this.refCount = this.channel.users.length;
|
||||||
} else {
|
} else {
|
||||||
this.channel.emit("empty");
|
this.channel.emit("empty");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
function Channel(name) {
|
function Channel(name) {
|
||||||
MakeEmitter(this);
|
MakeEmitter(this);
|
||||||
|
@ -54,7 +80,7 @@ function Channel(name) {
|
||||||
this.logger = new Logger.Logger(path.join(__dirname, "..", "..", "chanlogs",
|
this.logger = new Logger.Logger(path.join(__dirname, "..", "..", "chanlogs",
|
||||||
this.uniqueName + ".log"));
|
this.uniqueName + ".log"));
|
||||||
this.users = [];
|
this.users = [];
|
||||||
this.activeLock = new ActiveLock(this);
|
this.refCounter = new ReferenceCounter(this);
|
||||||
this.flags = 0;
|
this.flags = 0;
|
||||||
var self = this;
|
var self = this;
|
||||||
db.channels.load(this, function (err) {
|
db.channels.load(this, function (err) {
|
||||||
|
@ -238,15 +264,16 @@ Channel.prototype.saveState = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
Channel.prototype.checkModules = function (fn, args, cb) {
|
Channel.prototype.checkModules = function (fn, args, cb) {
|
||||||
var self = this;
|
const self = this;
|
||||||
|
const refCaller = `Channel::checkModules/${fn}`;
|
||||||
this.waitFlag(Flags.C_READY, function () {
|
this.waitFlag(Flags.C_READY, function () {
|
||||||
self.activeLock.lock();
|
self.refCounter.ref(refCaller);
|
||||||
var keys = Object.keys(self.modules);
|
var keys = Object.keys(self.modules);
|
||||||
var next = function (err, result) {
|
var next = function (err, result) {
|
||||||
if (result !== ChannelModule.PASSTHROUGH) {
|
if (result !== ChannelModule.PASSTHROUGH) {
|
||||||
/* Either an error occured, or the module denied the user access */
|
/* Either an error occured, or the module denied the user access */
|
||||||
cb(err, result);
|
cb(err, result);
|
||||||
self.activeLock.release();
|
self.refCounter.unref(refCaller);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +281,7 @@ Channel.prototype.checkModules = function (fn, args, cb) {
|
||||||
if (m === undefined) {
|
if (m === undefined) {
|
||||||
/* No more modules to check */
|
/* No more modules to check */
|
||||||
cb(null, ChannelModule.PASSTHROUGH);
|
cb(null, ChannelModule.PASSTHROUGH);
|
||||||
self.activeLock.release();
|
self.refCounter.unref(refCaller);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,13 +305,13 @@ Channel.prototype.notifyModules = function (fn, args) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Channel.prototype.joinUser = function (user, data) {
|
Channel.prototype.joinUser = function (user, data) {
|
||||||
var self = this;
|
const self = this;
|
||||||
|
|
||||||
self.activeLock.lock();
|
self.refCounter.ref("Channel::user");
|
||||||
self.waitFlag(Flags.C_READY, function () {
|
self.waitFlag(Flags.C_READY, function () {
|
||||||
/* User closed the connection before the channel finished loading */
|
/* User closed the connection before the channel finished loading */
|
||||||
if (user.socket.disconnected) {
|
if (user.socket.disconnected) {
|
||||||
self.activeLock.release();
|
self.refCounter.unref("Channel::user");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,7 +320,7 @@ Channel.prototype.joinUser = function (user, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
Logger.errlog.log("user.refreshAccount failed at Channel.joinUser");
|
Logger.errlog.log("user.refreshAccount failed at Channel.joinUser");
|
||||||
Logger.errlog.log(err.stack);
|
Logger.errlog.log(err.stack);
|
||||||
self.activeLock.release();
|
self.refCounter.unref("Channel::user");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,8 +331,10 @@ Channel.prototype.joinUser = function (user, data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function afterAccount() {
|
function afterAccount() {
|
||||||
if (self.dead || user.socket.disconnected) {
|
if (user.socket.disconnected) {
|
||||||
if (self.activeLock) self.activeLock.release();
|
self.refCounter.unref("Channel::user");
|
||||||
|
return;
|
||||||
|
} else if (self.dead) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,9 +347,7 @@ Channel.prototype.joinUser = function (user, data) {
|
||||||
} else {
|
} else {
|
||||||
user.account.channelRank = 0;
|
user.account.channelRank = 0;
|
||||||
user.account.effectiveRank = user.account.globalRank;
|
user.account.effectiveRank = user.account.globalRank;
|
||||||
if (self.activeLock) {
|
self.refCounter.unref("Channel::user");
|
||||||
self.activeLock.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -408,7 +435,7 @@ Channel.prototype.partUser = function (user) {
|
||||||
});
|
});
|
||||||
this.sendUsercount(this.users);
|
this.sendUsercount(this.users);
|
||||||
|
|
||||||
this.activeLock.release();
|
this.refCounter.unref("Channel::user");
|
||||||
user.die();
|
user.die();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -555,20 +582,20 @@ Channel.prototype.sendUserJoin = function (users, user) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Channel.prototype.readLog = function (cb) {
|
Channel.prototype.readLog = function (cb) {
|
||||||
var maxLen = 102400;
|
const maxLen = 102400;
|
||||||
var file = this.logger.filename;
|
const file = this.logger.filename;
|
||||||
this.activeLock.lock();
|
this.refCounter.ref("Channel::readLog");
|
||||||
var self = this;
|
const self = this;
|
||||||
fs.stat(file, function (err, data) {
|
fs.stat(file, function (err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
self.activeLock.release();
|
self.refCounter.unref("Channel::readLog");
|
||||||
return cb(err, null);
|
return cb(err, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
var start = Math.max(data.size - maxLen, 0);
|
const start = Math.max(data.size - maxLen, 0);
|
||||||
var end = data.size - 1;
|
const end = data.size - 1;
|
||||||
|
|
||||||
var read = fs.createReadStream(file, {
|
const read = fs.createReadStream(file, {
|
||||||
start: start,
|
start: start,
|
||||||
end: end
|
end: end
|
||||||
});
|
});
|
||||||
|
@ -579,7 +606,7 @@ Channel.prototype.readLog = function (cb) {
|
||||||
});
|
});
|
||||||
read.on("end", function () {
|
read.on("end", function () {
|
||||||
cb(null, buffer);
|
cb(null, buffer);
|
||||||
self.activeLock.release();
|
self.refCounter.unref("Channel::readLog");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -648,7 +675,7 @@ Channel.prototype.packInfo = function (isAdmin) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
data.activeLockCount = this.activeLock.count;
|
data.activeLockCount = this.refCounter.refCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
|
@ -73,9 +73,10 @@ KickBanModule.prototype.onUserPostJoin = function (user) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var chan = this.channel;
|
const chan = this.channel;
|
||||||
|
const refCaller = "KickBanModule::onUserPostJoin";
|
||||||
user.waitFlag(Flags.U_LOGGED_IN, function () {
|
user.waitFlag(Flags.U_LOGGED_IN, function () {
|
||||||
chan.activeLock.lock();
|
chan.refCounter.ref(refCaller);
|
||||||
db.channels.isNameBanned(chan.name, user.getName(), function (err, banned) {
|
db.channels.isNameBanned(chan.name, user.getName(), function (err, banned) {
|
||||||
if (!err && banned) {
|
if (!err && banned) {
|
||||||
user.kick("You are banned from this channel.");
|
user.kick("You are banned from this channel.");
|
||||||
|
@ -84,7 +85,7 @@ KickBanModule.prototype.onUserPostJoin = function (user) {
|
||||||
"name is banned)");
|
"name is banned)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chan.activeLock.release();
|
chan.refCounter.unref(refCaller);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -222,10 +223,10 @@ KickBanModule.prototype.handleCmdBan = function (user, msg, meta) {
|
||||||
var name = args.shift().toLowerCase();
|
var name = args.shift().toLowerCase();
|
||||||
var reason = args.join(" ");
|
var reason = args.join(" ");
|
||||||
|
|
||||||
var chan = this.channel;
|
const chan = this.channel;
|
||||||
chan.activeLock.lock();
|
chan.refCounter.ref("KickBanModule::handleCmdBan");
|
||||||
this.banName(user, name, reason, function (err) {
|
this.banName(user, name, reason, function (err) {
|
||||||
chan.activeLock.release();
|
chan.refCounter.unref("KickBanModule::handleCmdBan");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -249,10 +250,10 @@ KickBanModule.prototype.handleCmdIPBan = function (user, msg, meta) {
|
||||||
}
|
}
|
||||||
var reason = args.join(" ");
|
var reason = args.join(" ");
|
||||||
|
|
||||||
var chan = this.channel;
|
const chan = this.channel;
|
||||||
chan.activeLock.lock();
|
chan.refCounter.ref("KickBanModule::handleCmdIPBan");
|
||||||
this.banAll(user, name, range, reason, function (err) {
|
this.banAll(user, name, range, reason, function (err) {
|
||||||
chan.activeLock.release();
|
chan.refCounter.unref("KickBanModule::handleCmdIPBan");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -416,9 +417,10 @@ KickBanModule.prototype.handleUnban = function (user, data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
this.channel.activeLock.lock();
|
this.channel.refCounter.ref("KickBanModule::handleUnban");
|
||||||
db.channels.unbanId(this.channel.name, data.id, function (err) {
|
db.channels.unbanId(this.channel.name, data.id, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
self.channel.refCounter.unref("KickBanModule::handleUnban");
|
||||||
return user.socket.emit("errorMsg", {
|
return user.socket.emit("errorMsg", {
|
||||||
msg: err
|
msg: err
|
||||||
});
|
});
|
||||||
|
@ -431,7 +433,7 @@ KickBanModule.prototype.handleUnban = function (user, data) {
|
||||||
self.channel.modules.chat.sendModMessage(user.getName() + " unbanned " +
|
self.channel.modules.chat.sendModMessage(user.getName() + " unbanned " +
|
||||||
data.name, banperm);
|
data.name, banperm);
|
||||||
}
|
}
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("KickBanModule::handleUnban");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -51,16 +51,19 @@ LibraryModule.prototype.handleUncache = function (user, data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var chan = this.channel;
|
const chan = this.channel;
|
||||||
chan.activeLock.lock();
|
chan.refCounter.ref("LibraryModule::handleUncache");
|
||||||
db.channels.deleteFromLibrary(chan.name, data.id, function (err, res) {
|
db.channels.deleteFromLibrary(chan.name, data.id, function (err, res) {
|
||||||
if (chan.dead || err) {
|
if (chan.dead) {
|
||||||
|
return;
|
||||||
|
} else if (err) {
|
||||||
|
chan.refCounter.unref("LibraryModule::handleUncache");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
chan.logger.log("[library] " + user.getName() + " deleted " + data.id +
|
chan.logger.log("[library] " + user.getName() + " deleted " + data.id +
|
||||||
"from the library");
|
"from the library");
|
||||||
chan.activeLock.release();
|
chan.refCounter.unref("LibraryModule::handleUncache");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,15 @@ MediaRefresherModule.prototype.onPreMediaChange = function (data, cb) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MediaRefresherModule.prototype.unload = function () {
|
||||||
|
try {
|
||||||
|
clearInterval(this._interval);
|
||||||
|
this._interval = null;
|
||||||
|
} catch (error) {
|
||||||
|
Logger.errlog.log(error.stack);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
MediaRefresherModule.prototype.initGoogleDocs = function (data, cb) {
|
MediaRefresherModule.prototype.initGoogleDocs = function (data, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
self.refreshGoogleDocs(data, cb);
|
self.refreshGoogleDocs(data, cb);
|
||||||
|
@ -63,23 +72,26 @@ MediaRefresherModule.prototype.initVimeo = function (data, cb) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
const self = this;
|
||||||
self.channel.activeLock.lock();
|
self.channel.refCounter.ref("MediaRefresherModule::initVimeo");
|
||||||
Vimeo.extract(data.id).then(function (direct) {
|
Vimeo.extract(data.id).then(function (direct) {
|
||||||
if (self.dead || self.channel.dead)
|
if (self.dead || self.channel.dead) {
|
||||||
|
self.unload();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (self._media === data) {
|
if (self._media === data) {
|
||||||
data.meta.direct = direct;
|
data.meta.direct = direct;
|
||||||
self.channel.logger.log("[mediarefresher] Refreshed vimeo video with ID " +
|
self.channel.logger.log("[mediarefresher] Refreshed vimeo video with ID " +
|
||||||
data.id);
|
data.id);
|
||||||
}
|
}
|
||||||
self.channel.activeLock.release();
|
|
||||||
|
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
Logger.errlog.log("Unexpected vimeo::extract() fail: " + err.stack);
|
Logger.errlog.log("Unexpected vimeo::extract() fail: " + err.stack);
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
|
}).finally(() => {
|
||||||
|
self.channel.refCounter.unref("MediaRefresherModule::initVimeo");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -87,10 +99,11 @@ MediaRefresherModule.prototype.refreshGoogleDocs = function (media, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (self.dead || self.channel.dead) {
|
if (self.dead || self.channel.dead) {
|
||||||
|
self.unload();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.channel.activeLock.lock();
|
self.channel.refCounter.ref("MediaRefresherModule::refreshGoogleDocs");
|
||||||
InfoGetter.getMedia(media.id, "gd", function (err, data) {
|
InfoGetter.getMedia(media.id, "gd", function (err, data) {
|
||||||
if (self.dead || self.channel.dead) {
|
if (self.dead || self.channel.dead) {
|
||||||
return;
|
return;
|
||||||
|
@ -108,7 +121,7 @@ MediaRefresherModule.prototype.refreshGoogleDocs = function (media, cb) {
|
||||||
self.channel.logger.log("[mediarefresher] Google Docs refresh failed " +
|
self.channel.logger.log("[mediarefresher] Google Docs refresh failed " +
|
||||||
"(likely redirect to login page-- make sure it is shared " +
|
"(likely redirect to login page-- make sure it is shared " +
|
||||||
"correctly)");
|
"correctly)");
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("MediaRefresherModule::refreshGoogleDocs");
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
return;
|
return;
|
||||||
case "Access Denied":
|
case "Access Denied":
|
||||||
|
@ -119,7 +132,7 @@ MediaRefresherModule.prototype.refreshGoogleDocs = function (media, cb) {
|
||||||
case "Google Drive videos must be shared publicly":
|
case "Google Drive videos must be shared publicly":
|
||||||
self.channel.logger.log("[mediarefresher] Google Docs refresh failed: " +
|
self.channel.logger.log("[mediarefresher] Google Docs refresh failed: " +
|
||||||
err);
|
err);
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("MediaRefresherModule::refreshGoogleDocs");
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
|
@ -128,14 +141,14 @@ MediaRefresherModule.prototype.refreshGoogleDocs = function (media, cb) {
|
||||||
err);
|
err);
|
||||||
Logger.errlog.log("Google Docs refresh failed for ID " + media.id +
|
Logger.errlog.log("Google Docs refresh failed for ID " + media.id +
|
||||||
": " + err);
|
": " + err);
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("MediaRefresherModule::refreshGoogleDocs");
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (media !== self._media) {
|
if (media !== self._media) {
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("MediaRefresherModule::refreshGoogleDocs");
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -143,7 +156,7 @@ MediaRefresherModule.prototype.refreshGoogleDocs = function (media, cb) {
|
||||||
self.channel.logger.log("[mediarefresher] Refreshed Google Docs video with ID " +
|
self.channel.logger.log("[mediarefresher] Refreshed Google Docs video with ID " +
|
||||||
media.id);
|
media.id);
|
||||||
media.meta = data.meta;
|
media.meta = data.meta;
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("MediaRefresherModule::refreshGoogleDocs");
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -152,10 +165,11 @@ MediaRefresherModule.prototype.initGooglePlus = function (media, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (self.dead || self.channel.dead) {
|
if (self.dead || self.channel.dead) {
|
||||||
|
self.unload();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.channel.activeLock.lock();
|
self.channel.refCounter.ref("MediaRefresherModule::initGooglePlus");
|
||||||
InfoGetter.getMedia(media.id, "gp", function (err, data) {
|
InfoGetter.getMedia(media.id, "gp", function (err, data) {
|
||||||
if (self.dead || self.channel.dead) {
|
if (self.dead || self.channel.dead) {
|
||||||
return;
|
return;
|
||||||
|
@ -177,7 +191,7 @@ MediaRefresherModule.prototype.initGooglePlus = function (media, cb) {
|
||||||
"and is shared publicly"):
|
"and is shared publicly"):
|
||||||
self.channel.logger.log("[mediarefresher] Google+ refresh failed: " +
|
self.channel.logger.log("[mediarefresher] Google+ refresh failed: " +
|
||||||
err);
|
err);
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("MediaRefresherModule::initGooglePlus");
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
|
@ -186,14 +200,14 @@ MediaRefresherModule.prototype.initGooglePlus = function (media, cb) {
|
||||||
err);
|
err);
|
||||||
Logger.errlog.log("Google+ refresh failed for ID " + media.id +
|
Logger.errlog.log("Google+ refresh failed for ID " + media.id +
|
||||||
": " + err);
|
": " + err);
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("MediaRefresherModule::initGooglePlus");
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (media !== self._media) {
|
if (media !== self._media) {
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("MediaRefresherModule::initGooglePlus");
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -201,7 +215,7 @@ MediaRefresherModule.prototype.initGooglePlus = function (media, cb) {
|
||||||
self.channel.logger.log("[mediarefresher] Refreshed Google+ video with ID " +
|
self.channel.logger.log("[mediarefresher] Refreshed Google+ video with ID " +
|
||||||
media.id);
|
media.id);
|
||||||
media.meta = data.meta;
|
media.meta = data.meta;
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("MediaRefresherModule::initGooglePlus");
|
||||||
if (cb) cb();
|
if (cb) cb();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,13 @@ var CustomEmbedFilter = require("../customembed").filter;
|
||||||
var XSS = require("../xss");
|
var XSS = require("../xss");
|
||||||
|
|
||||||
const MAX_ITEMS = Config.get("playlist.max-items");
|
const MAX_ITEMS = Config.get("playlist.max-items");
|
||||||
|
// Limit requestPlaylist to once per 60 seconds
|
||||||
|
const REQ_PLAYLIST_THROTTLE = {
|
||||||
|
burst: 1,
|
||||||
|
sustained: 0,
|
||||||
|
cooldown: 60
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const TYPE_QUEUE = {
|
const TYPE_QUEUE = {
|
||||||
id: "string,boolean",
|
id: "string,boolean",
|
||||||
|
@ -216,9 +223,7 @@ PlaylistModule.prototype.onUserPostJoin = function (user) {
|
||||||
user.socket.on("playerReady", function () {
|
user.socket.on("playerReady", function () {
|
||||||
self.sendChangeMedia([user]);
|
self.sendChangeMedia([user]);
|
||||||
});
|
});
|
||||||
user.socket.on("requestPlaylist", function () {
|
user.socket.on("requestPlaylist", this.handleRequestPlaylist.bind(this, user));
|
||||||
self.sendPlaylist([user]);
|
|
||||||
});
|
|
||||||
user.on("login", function () {
|
user.on("login", function () {
|
||||||
self.sendPlaylist([user]);
|
self.sendPlaylist([user]);
|
||||||
});
|
});
|
||||||
|
@ -447,15 +452,15 @@ PlaylistModule.prototype.queueStandard = function (user, data) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var self = this;
|
const self = this;
|
||||||
this.channel.activeLock.lock();
|
this.channel.refCounter.ref("PlaylistModule::queueStandard");
|
||||||
this.semaphore.queue(function (lock) {
|
this.semaphore.queue(function (lock) {
|
||||||
var lib = self.channel.modules.library;
|
var lib = self.channel.modules.library;
|
||||||
if (lib && self.channel.is(Flags.C_REGISTERED) && !util.isLive(data.type)) {
|
if (lib && self.channel.is(Flags.C_REGISTERED) && !util.isLive(data.type)) {
|
||||||
lib.getItem(data.id, function (err, item) {
|
lib.getItem(data.id, function (err, item) {
|
||||||
if (err && err !== "Item not in library") {
|
if (err && err !== "Item not in library") {
|
||||||
error(err+"");
|
error(err+"");
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("PlaylistModule::queueStandard");
|
||||||
return lock.release();
|
return lock.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,7 +469,7 @@ PlaylistModule.prototype.queueStandard = function (user, data) {
|
||||||
data.shouldAddToLibrary = false;
|
data.shouldAddToLibrary = false;
|
||||||
self._addItem(item, data, user, function () {
|
self._addItem(item, data, user, function () {
|
||||||
lock.release();
|
lock.release();
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("PlaylistModule::queueStandard");
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
handleLookup();
|
handleLookup();
|
||||||
|
@ -479,25 +484,13 @@ PlaylistModule.prototype.queueStandard = function (user, data) {
|
||||||
InfoGetter.getMedia(data.id, data.type, function (err, media) {
|
InfoGetter.getMedia(data.id, data.type, function (err, media) {
|
||||||
if (err) {
|
if (err) {
|
||||||
error(XSS.sanitizeText(String(err)));
|
error(XSS.sanitizeText(String(err)));
|
||||||
if (self.channel && self.channel.activeLock) {
|
self.channel.refCounter.unref("PlaylistModule::queueStandard");
|
||||||
self.channel.activeLock.release();
|
|
||||||
} else {
|
|
||||||
Logger.errlog.log("Attempted release of channel lock after " +
|
|
||||||
"channel was already unloaded in queueStandard: " +
|
|
||||||
channelName + " " + data.type + ":" + data.id);
|
|
||||||
}
|
|
||||||
return lock.release();
|
return lock.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
self._addItem(media, data, user, function () {
|
self._addItem(media, data, user, function () {
|
||||||
lock.release();
|
lock.release();
|
||||||
if (self.channel && self.channel.activeLock) {
|
self.channel.refCounter.unref("PlaylistModule::queueStandard");
|
||||||
self.channel.activeLock.release();
|
|
||||||
} else {
|
|
||||||
Logger.errlog.log("Attempted release of channel lock after " +
|
|
||||||
"channel was already unloaded in queueStandard: " +
|
|
||||||
channelName + " " + data.type + ":" + data.id);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -536,12 +529,12 @@ PlaylistModule.prototype.queueYouTubePlaylist = function (user, data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.channel.activeLock.lock();
|
self.channel.refCounter.ref("PlaylistModule::queueYouTubePlaylist");
|
||||||
vids.forEach(function (media) {
|
vids.forEach(function (media) {
|
||||||
data.link = util.formatLink(media.id, media.type);
|
data.link = util.formatLink(media.id, media.type);
|
||||||
self._addItem(media, data, user);
|
self._addItem(media, data, user);
|
||||||
});
|
});
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("PlaylistModule::queueYouTubePlaylist");
|
||||||
|
|
||||||
lock.release();
|
lock.release();
|
||||||
});
|
});
|
||||||
|
@ -560,7 +553,7 @@ PlaylistModule.prototype.handleDelete = function (user, data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var plitem = this.items.find(data);
|
var plitem = this.items.find(data);
|
||||||
self.channel.activeLock.lock();
|
self.channel.refCounter.ref("PlaylistModule::handleDelete");
|
||||||
this.semaphore.queue(function (lock) {
|
this.semaphore.queue(function (lock) {
|
||||||
if (self._delete(data)) {
|
if (self._delete(data)) {
|
||||||
self.channel.logger.log("[playlist] " + user.getName() + " deleted " +
|
self.channel.logger.log("[playlist] " + user.getName() + " deleted " +
|
||||||
|
@ -568,7 +561,7 @@ PlaylistModule.prototype.handleDelete = function (user, data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
lock.release();
|
lock.release();
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("PlaylistModule::handleDelete");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -602,27 +595,27 @@ PlaylistModule.prototype.handleMoveMedia = function (user, data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
const self = this;
|
||||||
self.channel.activeLock.lock();
|
self.channel.refCounter.ref("PlaylistModule::handleMoveMedia");
|
||||||
self.semaphore.queue(function (lock) {
|
self.semaphore.queue(function (lock) {
|
||||||
if (!self.items.remove(data.from)) {
|
if (!self.items.remove(data.from)) {
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("PlaylistModule::handleMoveMedia");
|
||||||
return lock.release();
|
return lock.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.after === "prepend") {
|
if (data.after === "prepend") {
|
||||||
if (!self.items.prepend(from)) {
|
if (!self.items.prepend(from)) {
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("PlaylistModule::handleMoveMedia");
|
||||||
return lock.release();
|
return lock.release();
|
||||||
}
|
}
|
||||||
} else if (data.after === "append") {
|
} else if (data.after === "append") {
|
||||||
if (!self.items.append(from)) {
|
if (!self.items.append(from)) {
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("PlaylistModule::handleMoveMedia");
|
||||||
return lock.release();
|
return lock.release();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!self.items.insertAfter(from, data.after)) {
|
if (!self.items.insertAfter(from, data.after)) {
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("PlaylistModule::handleMoveMedia");
|
||||||
return lock.release();
|
return lock.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -633,7 +626,7 @@ PlaylistModule.prototype.handleMoveMedia = function (user, data) {
|
||||||
from.media.title +
|
from.media.title +
|
||||||
(after ? " after " + after.media.title : ""));
|
(after ? " after " + after.media.title : ""));
|
||||||
lock.release();
|
lock.release();
|
||||||
self.channel.activeLock.release();
|
self.channel.refCounter.unref("PlaylistModule::handleMoveMedia");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1128,55 +1121,6 @@ PlaylistModule.prototype._leadLoop = function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
PlaylistModule.prototype.refreshGoogleDocs = function (cb) {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
if (self.dead || !self.channel || self.channel.dead) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var abort = function () {
|
|
||||||
if (self.current) {
|
|
||||||
self.current.media.meta.object = self.current.media.meta.object || null;
|
|
||||||
self.current.media.meta.failed = true;
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!this.current || this.current.media.type !== "gd") {
|
|
||||||
return abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.channel.activeLock.lock();
|
|
||||||
InfoGetter.getMedia(this.current.media.id, "gd", function (err, media) {
|
|
||||||
if (err) {
|
|
||||||
Logger.errlog.log("Google Docs autorefresh failed: " + err);
|
|
||||||
Logger.errlog.log("ID was: " + self.current.media.id);
|
|
||||||
if (self.current) {
|
|
||||||
self.current.media.meta.object = self.current.media.meta.object || null;
|
|
||||||
self.current.media.meta.failed = true;
|
|
||||||
}
|
|
||||||
if (cb) {
|
|
||||||
cb();
|
|
||||||
}
|
|
||||||
self.channel.activeLock.release();
|
|
||||||
} else {
|
|
||||||
if (!self.current || self.current.media.type !== "gd") {
|
|
||||||
self.channel.activeLock.release();
|
|
||||||
return abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.current.media.meta = media.meta;
|
|
||||||
self.current.media.meta.expiration = Date.now() + 3600000;
|
|
||||||
self.channel.logger.log("[playlist] Auto-refreshed Google Doc video");
|
|
||||||
cb && cb();
|
|
||||||
self.channel.activeLock.release();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
PlaylistModule.prototype._playNext = function () {
|
PlaylistModule.prototype._playNext = function () {
|
||||||
if (!this.current) {
|
if (!this.current) {
|
||||||
return;
|
return;
|
||||||
|
@ -1335,10 +1279,11 @@ PlaylistModule.prototype.handleQueuePlaylist = function (user, data) {
|
||||||
pos: data.pos
|
pos: data.pos
|
||||||
};
|
};
|
||||||
|
|
||||||
var self = this;
|
const self = this;
|
||||||
self.channel.activeLock.lock();
|
self.channel.refCounter.ref("PlaylistModule::handleQueuePlaylist");
|
||||||
db.getUserPlaylist(user.getName(), data.name, function (err, pl) {
|
db.getUserPlaylist(user.getName(), data.name, function (err, pl) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
self.channel.refCounter.unref("PlaylistModule::handleQueuePlaylist");
|
||||||
return user.socket.emit("errorMsg", {
|
return user.socket.emit("errorMsg", {
|
||||||
msg: "Playlist load failed: " + err
|
msg: "Playlist load failed: " + err
|
||||||
});
|
});
|
||||||
|
@ -1369,7 +1314,6 @@ PlaylistModule.prototype.handleQueuePlaylist = function (user, data) {
|
||||||
var m = new Media(item.id, item.title, item.seconds, item.type, item.meta);
|
var m = new Media(item.id, item.title, item.seconds, item.type, item.meta);
|
||||||
self._addItem(m, qdata, user);
|
self._addItem(m, qdata, user);
|
||||||
});
|
});
|
||||||
self.channel.activeLock.release();
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.errlog.log("Loading user playlist failed!");
|
Logger.errlog.log("Loading user playlist failed!");
|
||||||
Logger.errlog.log("PL: " + user.getName() + "-" + data.name);
|
Logger.errlog.log("PL: " + user.getName() + "-" + data.name);
|
||||||
|
@ -1378,9 +1322,22 @@ PlaylistModule.prototype.handleQueuePlaylist = function (user, data) {
|
||||||
msg: "Internal error occurred when loading playlist.",
|
msg: "Internal error occurred when loading playlist.",
|
||||||
link: null
|
link: null
|
||||||
});
|
});
|
||||||
self.channel.activeLock.release();
|
} finally {
|
||||||
|
self.channel.refCounter.unref("PlaylistModule::handleQueuePlaylist");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PlaylistModule.prototype.handleRequestPlaylist = function (user) {
|
||||||
|
if (user.reqPlaylistLimiter.throttle(REQ_PLAYLIST_THROTTLE)) {
|
||||||
|
user.socket.emit("errorMsg", {
|
||||||
|
msg: "Get Playlist URLs is limited to 1 usage every 60 seconds. " +
|
||||||
|
"Please try again later.",
|
||||||
|
code: "REQ_PLAYLIST_LIMIT_REACHED"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.sendPlaylist([user]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = PlaylistModule;
|
module.exports = PlaylistModule;
|
||||||
|
|
|
@ -50,7 +50,8 @@ var defaults = {
|
||||||
io: {
|
io: {
|
||||||
domain: "http://localhost",
|
domain: "http://localhost",
|
||||||
"default-port": 1337,
|
"default-port": 1337,
|
||||||
"ip-connection-limit": 10
|
"ip-connection-limit": 10,
|
||||||
|
"per-message-deflate": false
|
||||||
},
|
},
|
||||||
mail: {
|
mail: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
|
|
@ -25,11 +25,20 @@ Socket.prototype.packet = function () {
|
||||||
exports.add('socket.io:packet');
|
exports.add('socket.io:packet');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getConnectedSockets() {
|
||||||
|
var sockets = io.instance.sockets.sockets;
|
||||||
|
if (typeof sockets.length === 'number') {
|
||||||
|
return sockets.length;
|
||||||
|
} else {
|
||||||
|
return Object.keys(sockets).length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setInterval(function () {
|
setInterval(function () {
|
||||||
try {
|
try {
|
||||||
counters['memory:rss'] = process.memoryUsage().rss / 1048576;
|
counters['memory:rss'] = process.memoryUsage().rss / 1048576;
|
||||||
counters['load:1min'] = os.loadavg()[0];
|
counters['load:1min'] = os.loadavg()[0];
|
||||||
counters['socket.io:count'] = io.instance.sockets.sockets.length;
|
counters['socket.io:count'] = getConnectedSockets();
|
||||||
counterLog.log(JSON.stringify(counters));
|
counterLog.log(JSON.stringify(counters));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.errlog.log(e.stack);
|
Logger.errlog.log(e.stack);
|
||||||
|
|
|
@ -583,7 +583,7 @@ module.exports.loadAnnouncement = function () {
|
||||||
var sv = Server.getServer();
|
var sv = Server.getServer();
|
||||||
sv.announcement = announcement;
|
sv.announcement = announcement;
|
||||||
for (var id in sv.ioServers) {
|
for (var id in sv.ioServers) {
|
||||||
sv.ioServers[id].sockets.emit("announcement", announcement);
|
sv.ioServers[id].emit("announcement", announcement);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,7 @@ var Config = require("./config");
|
||||||
var ffmpeg = require("./ffmpeg");
|
var ffmpeg = require("./ffmpeg");
|
||||||
var mediaquery = require("cytube-mediaquery");
|
var mediaquery = require("cytube-mediaquery");
|
||||||
var YouTube = require("cytube-mediaquery/lib/provider/youtube");
|
var YouTube = require("cytube-mediaquery/lib/provider/youtube");
|
||||||
|
var Vimeo = require("cytube-mediaquery/lib/provider/vimeo");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Preference map of quality => youtube formats.
|
* Preference map of quality => youtube formats.
|
||||||
|
@ -158,50 +159,11 @@ var Getters = {
|
||||||
return Getters.vi_oauth(id, callback);
|
return Getters.vi_oauth(id, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
var options = {
|
Vimeo.lookup(id).then(video => {
|
||||||
host: "vimeo.com",
|
video = new Media(video.id, video.title, video.duration, "vi");
|
||||||
port: 443,
|
callback(null, video);
|
||||||
path: "/api/v2/video/" + id + ".json",
|
}).catch(error => {
|
||||||
method: "GET",
|
callback(error.message);
|
||||||
dataType: "jsonp",
|
|
||||||
timeout: 1000
|
|
||||||
};
|
|
||||||
|
|
||||||
urlRetrieve(https, options, function (status, data) {
|
|
||||||
switch (status) {
|
|
||||||
case 200:
|
|
||||||
break; /* Request is OK, skip to handling data */
|
|
||||||
case 400:
|
|
||||||
return callback("Invalid request", null);
|
|
||||||
case 403:
|
|
||||||
return callback("Private video", null);
|
|
||||||
case 404:
|
|
||||||
return callback("Video not found", null);
|
|
||||||
case 500:
|
|
||||||
case 503:
|
|
||||||
return callback("Service unavailable", null);
|
|
||||||
default:
|
|
||||||
return callback("HTTP " + status, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
data = JSON.parse(data);
|
|
||||||
data = data[0];
|
|
||||||
var seconds = data.duration;
|
|
||||||
var title = data.title;
|
|
||||||
var media = new Media(id, title, seconds, "vi");
|
|
||||||
callback(false, media);
|
|
||||||
} catch(e) {
|
|
||||||
var err = e;
|
|
||||||
/**
|
|
||||||
* This should no longer be necessary as the outer handler
|
|
||||||
* checks for HTTP 404
|
|
||||||
*/
|
|
||||||
if (buffer.match(/not found/))
|
|
||||||
err = "Video not found";
|
|
||||||
|
|
||||||
callback(err, null);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -242,6 +242,9 @@ function handleConnection(sock) {
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init: function (srv, webConfig) {
|
init: function (srv, webConfig) {
|
||||||
var bound = {};
|
var bound = {};
|
||||||
|
const ioOptions = {
|
||||||
|
perMessageDeflate: Config.get("io.per-message-deflate")
|
||||||
|
};
|
||||||
var io = sio.instance = sio();
|
var io = sio.instance = sio();
|
||||||
|
|
||||||
io.use(handleAuth);
|
io.use(handleAuth);
|
||||||
|
@ -259,7 +262,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id in srv.servers) {
|
if (id in srv.servers) {
|
||||||
io.attach(srv.servers[id]);
|
io.attach(srv.servers[id], ioOptions);
|
||||||
} else {
|
} else {
|
||||||
var server = require("http").createServer().listen(bind.port, bind.ip);
|
var server = require("http").createServer().listen(bind.port, bind.ip);
|
||||||
server.on("clientError", function (err, socket) {
|
server.on("clientError", function (err, socket) {
|
||||||
|
@ -268,7 +271,7 @@ module.exports = {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
io.attach(server);
|
io.attach(server, ioOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
bound[id] = null;
|
bound[id] = null;
|
||||||
|
|
|
@ -207,6 +207,24 @@ Server.prototype.unloadChannel = function (chan) {
|
||||||
chan.notifyModules("unload", []);
|
chan.notifyModules("unload", []);
|
||||||
Object.keys(chan.modules).forEach(function (k) {
|
Object.keys(chan.modules).forEach(function (k) {
|
||||||
chan.modules[k].dead = true;
|
chan.modules[k].dead = true;
|
||||||
|
/*
|
||||||
|
* Automatically clean up any timeouts/intervals assigned
|
||||||
|
* to properties of channel modules. Prevents a memory leak
|
||||||
|
* in case of forgetting to clear the timer on the "unload"
|
||||||
|
* module event.
|
||||||
|
*/
|
||||||
|
Object.keys(chan.modules[k]).forEach(function (prop) {
|
||||||
|
if (chan.modules[k][prop] && chan.modules[k][prop]._onTimeout) {
|
||||||
|
Logger.errlog.log("Warning: detected non-null timer when unloading " +
|
||||||
|
"module " + k + ": " + prop);
|
||||||
|
try {
|
||||||
|
clearTimeout(chan.modules[k][prop]);
|
||||||
|
clearInterval(chan.modules[k][prop]);
|
||||||
|
} catch (error) {
|
||||||
|
Logger.errlog.log(error.stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
for (var i = 0; i < this.channels.length; i++) {
|
for (var i = 0; i < this.channels.length; i++) {
|
||||||
|
@ -220,8 +238,10 @@ Server.prototype.unloadChannel = function (chan) {
|
||||||
// Empty all outward references from the channel
|
// Empty all outward references from the channel
|
||||||
var keys = Object.keys(chan);
|
var keys = Object.keys(chan);
|
||||||
for (var i in keys) {
|
for (var i in keys) {
|
||||||
|
if (keys[i] !== "refCounter") {
|
||||||
delete chan[keys[i]];
|
delete chan[keys[i]];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
chan.dead = true;
|
chan.dead = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ function User(socket) {
|
||||||
self.channel = null;
|
self.channel = null;
|
||||||
self.queueLimiter = util.newRateLimiter();
|
self.queueLimiter = util.newRateLimiter();
|
||||||
self.chatLimiter = util.newRateLimiter();
|
self.chatLimiter = util.newRateLimiter();
|
||||||
|
self.reqPlaylistLimiter = util.newRateLimiter();
|
||||||
self.awaytimer = false;
|
self.awaytimer = false;
|
||||||
|
|
||||||
var announcement = Server.getServer().announcement;
|
var announcement = Server.getServer().announcement;
|
||||||
|
|
|
@ -27,7 +27,7 @@ mixin navdefaultlinks(page)
|
||||||
b.caret
|
b.caret
|
||||||
ul.dropdown-menu
|
ul.dropdown-menu
|
||||||
if loggedIn
|
if loggedIn
|
||||||
li: a(href="/logout?dest=#{encodeURIComponent(baseUrl + page)}&_csrf=#{csrfToken}") Logout
|
li: a(href="javascript:$('#logoutform').submit();") Log out
|
||||||
li.divider
|
li.divider
|
||||||
li: a(href="#{loginDomain}/account/channels") Channels
|
li: a(href="#{loginDomain}/account/channels") Channels
|
||||||
li: a(href="#{loginDomain}/account/profile") Profile
|
li: a(href="#{loginDomain}/account/profile") Profile
|
||||||
|
@ -72,5 +72,5 @@ mixin navlogoutform(redirect)
|
||||||
input(type="hidden", name="_csrf", value=csrfToken)
|
input(type="hidden", name="_csrf", value=csrfToken)
|
||||||
span#welcome Welcome, #{loginName}
|
span#welcome Welcome, #{loginName}
|
||||||
span ·
|
span ·
|
||||||
input#logout.navbar-link(type="submit", value="Logout")
|
input#logout.navbar-link(type="submit", value="Log out")
|
||||||
|
|
||||||
|
|
|
@ -654,7 +654,7 @@
|
||||||
case 'mp3':
|
case 'mp3':
|
||||||
return 'audio/mp3';
|
return 'audio/mp3';
|
||||||
case 'vorbis':
|
case 'vorbis':
|
||||||
return 'audio/vorbis';
|
return 'audio/ogg';
|
||||||
default:
|
default:
|
||||||
return 'video/flv';
|
return 'video/flv';
|
||||||
}
|
}
|
||||||
|
|
27
www/js/ui.js
27
www/js/ui.js
|
@ -490,9 +490,14 @@ $("#voteskip").click(function() {
|
||||||
$("#getplaylist").click(function() {
|
$("#getplaylist").click(function() {
|
||||||
var callback = function(data) {
|
var callback = function(data) {
|
||||||
hidePlayer();
|
hidePlayer();
|
||||||
socket.listeners("playlist").splice(
|
var idx = socket.listeners("errorMsg").indexOf(errCallback);
|
||||||
socket.listeners("playlist").indexOf(callback)
|
if (idx >= 0) {
|
||||||
);
|
socket.listeners("errorMsg").splice(idx);
|
||||||
|
}
|
||||||
|
idx = socket.listeners("playlist").indexOf(callback);
|
||||||
|
if (idx >= 0) {
|
||||||
|
socket.listeners("playlist").splice(idx);
|
||||||
|
}
|
||||||
var list = [];
|
var list = [];
|
||||||
for(var i = 0; i < data.length; i++) {
|
for(var i = 0; i < data.length; i++) {
|
||||||
var entry = formatURL(data[i].media);
|
var entry = formatURL(data[i].media);
|
||||||
|
@ -524,6 +529,22 @@ $("#getplaylist").click(function() {
|
||||||
outer.modal();
|
outer.modal();
|
||||||
};
|
};
|
||||||
socket.on("playlist", callback);
|
socket.on("playlist", callback);
|
||||||
|
var errCallback = function(data) {
|
||||||
|
if (data.code !== "REQ_PLAYLIST_LIMIT_REACHED") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var idx = socket.listeners("errorMsg").indexOf(errCallback);
|
||||||
|
if (idx >= 0) {
|
||||||
|
socket.listeners("errorMsg").splice(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = socket.listeners("playlist").indexOf(callback);
|
||||||
|
if (idx >= 0) {
|
||||||
|
socket.listeners("playlist").splice(idx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
socket.on("errorMsg", errCallback);
|
||||||
socket.emit("requestPlaylist");
|
socket.emit("requestPlaylist");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1575,28 +1575,7 @@ function pingMessage(isHighlight) {
|
||||||
|
|
||||||
/* layouts */
|
/* layouts */
|
||||||
|
|
||||||
function compactLayout() {
|
function undoHDLayout() {
|
||||||
/* Undo synchtube layout */
|
|
||||||
if ($("body").hasClass("synchtube")) {
|
|
||||||
$("body").removeClass("synchtube")
|
|
||||||
$("#chatwrap").detach().insertBefore($("#videowrap"));
|
|
||||||
$("#leftcontrols").detach().insertBefore($("#rightcontrols"));
|
|
||||||
$("#leftpane").detach().insertBefore($("#rightpane"));
|
|
||||||
$("#userlist").css("float", "left");
|
|
||||||
if($("#userlisttoggle").hasClass("glyphicon-chevron-left")){
|
|
||||||
$("#userlisttoggle").removeClass("glyphicon-chevron-left").addClass("glyphicon-chevron-right")
|
|
||||||
}
|
|
||||||
$("#userlisttoggle").removeClass("pull-right").addClass("pull-left")
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Undo fluid layout */
|
|
||||||
if ($("body").hasClass("fluid")) {
|
|
||||||
$("body").removeClass("fluid")
|
|
||||||
$(".container-fluid").removeClass("container-fluid").addClass("container");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Undo HD layout */
|
|
||||||
if ($("body").hasClass("hd")) {
|
|
||||||
$("body").removeClass("hd");
|
$("body").removeClass("hd");
|
||||||
$("#drinkbar").detach().removeClass().addClass("col-lg-12 col-md-12")
|
$("#drinkbar").detach().removeClass().addClass("col-lg-12 col-md-12")
|
||||||
.appendTo("#drinkbarwrap");
|
.appendTo("#drinkbarwrap");
|
||||||
|
@ -1627,6 +1606,31 @@ function compactLayout() {
|
||||||
$("#mainpage").css("padding-top", "60px");
|
$("#mainpage").css("padding-top", "60px");
|
||||||
$("#queue").css("max-height", "500px");
|
$("#queue").css("max-height", "500px");
|
||||||
$("#messagebuffer, #userlist").css("max-height", "");
|
$("#messagebuffer, #userlist").css("max-height", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function compactLayout() {
|
||||||
|
/* Undo synchtube layout */
|
||||||
|
if ($("body").hasClass("synchtube")) {
|
||||||
|
$("body").removeClass("synchtube")
|
||||||
|
$("#chatwrap").detach().insertBefore($("#videowrap"));
|
||||||
|
$("#leftcontrols").detach().insertBefore($("#rightcontrols"));
|
||||||
|
$("#leftpane").detach().insertBefore($("#rightpane"));
|
||||||
|
$("#userlist").css("float", "left");
|
||||||
|
if($("#userlisttoggle").hasClass("glyphicon-chevron-left")){
|
||||||
|
$("#userlisttoggle").removeClass("glyphicon-chevron-left").addClass("glyphicon-chevron-right")
|
||||||
|
}
|
||||||
|
$("#userlisttoggle").removeClass("pull-right").addClass("pull-left")
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Undo fluid layout */
|
||||||
|
if ($("body").hasClass("fluid")) {
|
||||||
|
$("body").removeClass("fluid")
|
||||||
|
$(".container-fluid").removeClass("container-fluid").addClass("container");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Undo HD layout */
|
||||||
|
if ($("body").hasClass("hd")) {
|
||||||
|
undoHDLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
$("body").addClass("compact");
|
$("body").addClass("compact");
|
||||||
|
@ -1634,6 +1638,9 @@ function compactLayout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function fluidLayout() {
|
function fluidLayout() {
|
||||||
|
if ($("body").hasClass("hd")) {
|
||||||
|
undoHDLayout();
|
||||||
|
}
|
||||||
$(".container").removeClass("container").addClass("container-fluid");
|
$(".container").removeClass("container").addClass("container-fluid");
|
||||||
$("footer .container-fluid").removeClass("container-fluid").addClass("container");
|
$("footer .container-fluid").removeClass("container-fluid").addClass("container");
|
||||||
$("body").addClass("fluid");
|
$("body").addClass("fluid");
|
||||||
|
@ -1641,6 +1648,9 @@ function fluidLayout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function synchtubeLayout() {
|
function synchtubeLayout() {
|
||||||
|
if ($("body").hasClass("hd")) {
|
||||||
|
undoHDLayout();
|
||||||
|
}
|
||||||
if($("#userlisttoggle").hasClass("glyphicon-chevron-right")){
|
if($("#userlisttoggle").hasClass("glyphicon-chevron-right")){
|
||||||
$("#userlisttoggle").removeClass("glyphicon-chevron-right").addClass("glyphicon-chevron-left")
|
$("#userlisttoggle").removeClass("glyphicon-chevron-right").addClass("glyphicon-chevron-left")
|
||||||
}
|
}
|
||||||
|
@ -1652,6 +1662,9 @@ function synchtubeLayout() {
|
||||||
$("body").addClass("synchtube");
|
$("body").addClass("synchtube");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "HD" is kind of a misnomer. Should be renamed at some point.
|
||||||
|
*/
|
||||||
function hdLayout() {
|
function hdLayout() {
|
||||||
var videowrap = $("#videowrap"),
|
var videowrap = $("#videowrap"),
|
||||||
chatwrap = $("#chatwrap"),
|
chatwrap = $("#chatwrap"),
|
||||||
|
|
Loading…
Reference in New Issue