diff --git a/channel.js b/channel.js index 466cf66d..e635581e 100644 --- a/channel.js +++ b/channel.js @@ -144,112 +144,130 @@ Channel.prototype.hasPermission = function(user, key) { /* REGION Channel data */ Channel.prototype.loadDump = function() { - fs.readFile("chandump/" + this.name, function(err, data) { - if(err) { - if(err.code == "ENOENT") { - Logger.errlog.log("WARN: missing dump for " + this.name); - this.initialized = true; - this.saveDump(); + var self = this; + if(self.name === "") + return; + fs.stat("chandump/" + self.name, function (err, stats) { + if(!err) { + var mb = stats.size / 1048576; + mb = parseInt(mb * 100) / 100; + if(mb > 1) { + Logger.errlog.log("Large chandump detected: " + self.name + + " (" + mb + " MB)"); + self.updateMotd("Your channel file has exceeded the " + + "maximum size of 1MB and cannot be " + + "loaded. Please ask an administrator " + + "for assistance in restoring it."); + return; } - else { - Logger.errlog.log("Failed to open channel dump " + this.name); - Logger.errlog.log(err); - } - return; } - try { - this.logger.log("*** Loading channel dump from disk"); - data = JSON.parse(data); - /* Load the playlist */ - - // Old - if(data.queue) { - if(data.position < 0) - data.position = 0; - for(var i = 0; i < data.queue.length; i++) { - var e = data.queue[i]; - var m = new Media(e.id, e.title, e.seconds, e.type); - var p = this.playlist.makeItem(m); - p.queueby = data.queue[i].queueby ? data.queue[i].queueby - : ""; - p.temp = e.temp; - this.playlist.items.append(p); - if(i == data.position) - this.playlist.current = p; - } - this.sendAll("playlist", this.playlist.items.toArray()); - this.broadcastPlaylistMeta(); - this.playlist.startPlayback(); - } - // Current - else if(data.playlist) { - var chan = this; - this.playlist.load(data.playlist, function() { - chan.sendAll("playlist", chan.playlist.items.toArray()); - chan.broadcastPlaylistMeta(); - chan.playlist.startPlayback(data.playlist.time); - }); - } - for(var key in data.opts) { - // Gotta love backwards compatibility - if(key == "customcss" || key == "customjs") { - var k = key.substring(6); - this.opts[k] = data.opts[key]; + fs.readFile("chandump/" + self.name, function(err, data) { + if(err) { + if(err.code == "ENOENT") { + Logger.errlog.log("WARN: missing dump for " + self.name); + self.initialized = true; + self.saveDump(); } else { - this.opts[key] = data.opts[key]; + Logger.errlog.log("Failed to open channel dump " + self.name); + Logger.errlog.log(err); } + return; } - for(var key in data.permissions) { - this.permissions[key] = data.permissions[key]; - } - this.sendAll("setPermissions", this.permissions); - this.broadcastOpts(); - this.users.forEach(function (u) { - u.autoAFK(); - }); - if(data.filters) { - for(var i = 0; i < data.filters.length; i++) { - var f = data.filters[i]; - // Backwards compatibility - if(f[0] != undefined) { - var filt = new Filter("", f[0], "g", f[1]); - filt.active = f[2]; - this.updateFilter(filt, false); + try { + self.logger.log("*** Loading channel dump from disk"); + data = JSON.parse(data); + /* Load the playlist */ + + // Old + if(data.queue) { + if(data.position < 0) + data.position = 0; + for(var i = 0; i < data.queue.length; i++) { + var e = data.queue[i]; + var m = new Media(e.id, e.title, e.seconds, e.type); + var p = self.playlist.makeItem(m); + p.queueby = data.queue[i].queueby ? data.queue[i].queueby + : ""; + p.temp = e.temp; + self.playlist.items.append(p); + if(i == data.position) + self.playlist.current = p; + } + self.sendAll("playlist", self.playlist.items.toArray()); + self.broadcastPlaylistMeta(); + self.playlist.startPlayback(); + } + // Current + else if(data.playlist) { + var chan = self; + self.playlist.load(data.playlist, function() { + chan.sendAll("playlist", chan.playlist.items.toArray()); + chan.broadcastPlaylistMeta(); + chan.playlist.startPlayback(data.playlist.time); + }); + } + for(var key in data.opts) { + // Gotta love backwards compatibility + if(key == "customcss" || key == "customjs") { + var k = key.substring(6); + self.opts[k] = data.opts[key]; } else { - var filt = new Filter(f.name, f.source, f.flags, f.replace); - filt.active = f.active; - filt.filterlinks = f.filterlinks; - this.updateFilter(filt, false); + self.opts[key] = data.opts[key]; } } - this.broadcastChatFilters(); + for(var key in data.permissions) { + self.permissions[key] = data.permissions[key]; + } + self.sendAll("setPermissions", self.permissions); + self.broadcastOpts(); + self.users.forEach(function (u) { + u.autoAFK(); + }); + if(data.filters) { + for(var i = 0; i < data.filters.length; i++) { + var f = data.filters[i]; + // Backwards compatibility + if(f[0] != undefined) { + var filt = new Filter("", f[0], "g", f[1]); + filt.active = f[2]; + self.updateFilter(filt, false); + } + else { + var filt = new Filter(f.name, f.source, f.flags, f.replace); + filt.active = f.active; + filt.filterlinks = f.filterlinks; + self.updateFilter(filt, false); + } + } + self.broadcastChatFilters(); + } + if(data.motd) { + self.motd = data.motd; + self.broadcastMotd(); + } + self.setLock(!(data.openqueue || false)); + self.chatbuffer = data.chatbuffer || []; + for(var i = 0; i < self.chatbuffer.length; i++) { + self.sendAll("chatMsg", self.chatbuffer[i]); + } + self.css = data.css || ""; + self.js = data.js || ""; + self.sendAll("channelCSSJS", {css: self.css, js: self.js}); + self.initialized = true; + setTimeout(function() { incrementalDump(self); }.bind(self), 300000); } - if(data.motd) { - this.motd = data.motd; - this.broadcastMotd(); + catch(e) { + Logger.errlog.log("Channel dump load failed: "); + Logger.errlog.log(e.stack); } - this.setLock(!(data.openqueue || false)); - this.chatbuffer = data.chatbuffer || []; - for(var i = 0; i < this.chatbuffer.length; i++) { - this.sendAll("chatMsg", this.chatbuffer[i]); - } - this.css = data.css || ""; - this.js = data.js || ""; - this.sendAll("channelCSSJS", {css: this.css, js: this.js}); - this.initialized = true; - setTimeout(function() { incrementalDump(this); }.bind(this), 300000); - } - catch(e) { - Logger.errlog.log("Channel dump load failed: "); - Logger.errlog.log(e.stack); - } - }.bind(this)); + }); + }); } Channel.prototype.saveDump = function() { - if(!this.initialized) + if(!this.initialized || this.name === "") return; var filts = new Array(this.filters.length); for(var i = 0; i < this.filters.length; i++) { @@ -1184,7 +1202,19 @@ Channel.prototype.tryQueue = function(user, data) { if(user.rank < Rank.Moderator && this.leader != user - && user.noflood("queue", 1.5)) { + && user.noflood("queue", 3)) { + return; + } + + var count = this.playlist.count(data.id); + + if(user.rank < Rank.Moderator && count >= 5) { + user.socket.emit("queueFail", "That video is already on the " + + "playlist 5 times."); + return; + } else if(user.rank < Rank.Siteadmin && count >= 20) { + user.socket.emit("queueFail", "That video is already on the " + + "playlist 20 times."); return; } diff --git a/get-info.js b/get-info.js index c545926d..db94c16e 100644 --- a/get-info.js +++ b/get-info.js @@ -11,13 +11,23 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI var http = require("http"); var https = require("https"); +var domain = require("domain"); var Logger = require("./logger.js"); var Media = require("./media.js").Media; var CustomEmbedFilter = require("./customembed").filter; module.exports = function (Server) { - function urlRetrieve(transport, options, callback) { - try { + var urlRetrieve = function (transport, options, callback) { + // Catch any errors that crop up along the way of the request + // in order to prevent them from reaching the global handler. + // This should cut down on needing to restart the server + var d = domain.create(); + d.on("error", function (err) { + Logger.errlog.log("urlRetrieve failed: " + err); + Logger.errlog.log("Request was: " + options.host + options.path); + callback(503, err); + }); + d.run(function () { var req = transport.request(options, function (res) { var buffer = ""; res.setEncoding("utf-8"); @@ -30,10 +40,8 @@ module.exports = function (Server) { }); req.end(); - } catch(e) { - callback(503, ""); - } - } + }); + }; var Getters = { /* youtube.com */ @@ -65,6 +73,9 @@ module.exports = function (Server) { } else if(status === 403) { callback("Private video", null); return; + } else if(status === 503) { + callback("API failure", null); + return; } else if(status !== 200) { callback(true, null); return; @@ -177,6 +188,9 @@ module.exports = function (Server) { } else if(status === 403) { callback("Playlist is private", null); return; + } else if(status === 503) { + callback("API failure", null); + return; } else if(status !== 200) { callback(true, null); } @@ -278,6 +292,9 @@ module.exports = function (Server) { } else if(status === 403) { callback("Private video", null); return; + } else if(status === 503) { + callback("API failure", null); + return; } else if(status !== 200) { callback(true, null); return; @@ -362,6 +379,9 @@ module.exports = function (Server) { if(status === 404) { callback("Sound not found", null); return; + } else if(status === 503) { + callback("API failure", null); + return; } else if(status !== 302) { callback(true, null); return; diff --git a/media.js b/media.js index 3febcf6e..6944b094 100644 --- a/media.js +++ b/media.js @@ -45,6 +45,8 @@ exports.formatTime = formatTime; var Media = function(id, title, seconds, type) { this.id = id; this.title = title; + if(this.title.length > 100) + this.title = this.title.substring(0, 97) + "..."; this.seconds = seconds == "--:--" ? "--:--" : parseInt(seconds); this.duration = formatTime(this.seconds); if(seconds == "--:--") { diff --git a/package.json b/package.json index 320f7c7f..d11d3b19 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Calvin Montgomery", "name": "CyTube", "description": "Online media synchronizer and chat", - "version": "2.3.2", + "version": "2.3.3", "repository": { "url": "http://github.com/calzoneman/sync" }, diff --git a/playlist.js b/playlist.js index c91de926..99d4968e 100644 --- a/playlist.js +++ b/playlist.js @@ -450,6 +450,15 @@ Playlist.prototype.clear = function() { clearInterval(this._leadInterval); } +Playlist.prototype.count = function (id) { + var count = 0; + this.items.forEach(function (i) { + if(i.media.id === id) + count++; + }); + return count; +} + Playlist.prototype.lead = function(lead) { this.leading = lead; var pl = this; diff --git a/server.js b/server.js index 2bc42bf1..ced4da27 100644 --- a/server.js +++ b/server.js @@ -6,7 +6,7 @@ var Logger = require("./logger"); var Channel = require("./channel"); var User = require("./user"); -const VERSION = "2.3.2"; +const VERSION = "2.3.3"; function getIP(req) { var raw = req.connection.remoteAddress; @@ -63,6 +63,7 @@ var Server = { } } chan.name = ""; + chan.canonical_name = ""; }, stats: null, app: null, diff --git a/ullist.js b/ullist.js index 4538c4b0..9314fb9e 100644 --- a/ullist.js +++ b/ullist.js @@ -161,4 +161,13 @@ ULList.prototype.toArray = function(pack) { return arr; } +/* iterate across the playlist */ +ULList.prototype.forEach = function (fn) { + var item = this.first; + while(item !== null) { + fn(item); + item = item.next; + } +}; + exports.ULList = ULList; diff --git a/update.js b/update.js index 980ec796..ba8af53d 100644 --- a/update.js +++ b/update.js @@ -1,38 +1,14 @@ var Config = require("./config.js"); var Database = require("./database.js"); -//Config.DEBUG = true; -Database.setup(Config); -Database.init(); -var query; -var db = Database.getConnection(); +var x = {}; +Config.load(x, "cfg.json", function () { + Database.setup(x.cfg); + Database.init(); + var query; + var db = Database.getConnection(); -// Check for already existing -query = "SELECT owner FROM channels WHERE 1"; -if(!db.querySync(query)) { - query = "ALTER TABLE channels ADD owner VARCHAR(20) NOT NULL"; - var res = db.querySync(query); - if(!res) { - console.log(db); - console.log("Update failed!"); - } - else { - populateChannelOwners(); - } -} -console.log("Fixing user playlist bug"); -query = "ALTER TABLE user_playlists DROP PRIMARY KEY, ADD PRIMARY KEY (user, name)"; -if(!db.querySync(query)) { - console.log("Something went wrong"); -} -else { - console.log("fixed"); -} -db.closeSync(); -process.exit(0); - -function populateChannelOwners() { query = "SELECT * FROM channels WHERE 1"; var res = db.querySync(query); if(!res) { @@ -44,27 +20,16 @@ function populateChannelOwners() { var channels = res.fetchAllSync(); channels.forEach(function(chan) { chan = chan.name; - query = "SELECT name FROM `chan_"+chan+"_ranks` WHERE rank>=10 ORDER BY rank"; + query = "UPDATE `chan_" + chan + "_library` SET title=CONCAT(" + + "SUBSTRING(title FROM 0 FOR 97), '...') WHERE " + + "LENGTH(title) > 100"; + console.log(query); res = db.querySync(query); if(!res) { console.log(db); console.log("failed to fix "+chan); return; } - - var results = res.fetchAllSync(); - if(results.length == 0) { - console.log("bad channel: " + chan); - return; - } - var owner = results[0].name; - query = "UPDATE channels SET owner='"+owner+"' WHERE name='"+chan+"'"; - console.log("setting owner=" + owner + " for /r/" + chan); - res = db.querySync(query); - if(!res) { - console.log(db); - console.log("Update failed!"); - return; - } }); -} + db.closeSync(); +}); diff --git a/www/assets/css/ytsync.css b/www/assets/css/ytsync.css index 44e3d812..d78a9b99 100644 --- a/www/assets/css/ytsync.css +++ b/www/assets/css/ytsync.css @@ -159,7 +159,7 @@ html, body { } #messagebuffer div, #messagebuffer code, #filteredit code, -#channeldata td, #currenttitle { +#channeldata td, #currenttitle, .profile-box { white-space: pre-wrap; /* css-3 */ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ white-space: -pre-wrap; /* Opera 4-6 */ diff --git a/www/assets/js/callbacks.js b/www/assets/js/callbacks.js index 6ac940bf..b35ab18b 100644 --- a/www/assets/js/callbacks.js +++ b/www/assets/js/callbacks.js @@ -785,7 +785,7 @@ Callbacks = { } makeAlert("Error", data, "alert-error") .addClass("span12") - .insertAfter($("#mediaurl").parent()); + .insertBefore($("#extended_controls")); }, setTemp: function(data) { diff --git a/www/assets/js/data.js b/www/assets/js/data.js index f7948d08..c413fab3 100644 --- a/www/assets/js/data.js +++ b/www/assets/js/data.js @@ -117,7 +117,8 @@ var USEROPTS = { ignore_channelcss : getOrDefault("ignore_channelcss", false), ignore_channeljs : getOrDefault("ignore_channeljs", false), sort_rank : getOrDefault("sort_rank", false), - sort_afk : getOrDefault("sort_afk", false) + sort_afk : getOrDefault("sort_afk", false), + default_quality : getOrDefault("default_quality", "#quality_auto") }; var NO_WEBSOCKETS = USEROPTS.altsocket; diff --git a/www/assets/js/ui.js b/www/assets/js/ui.js index 399a8c6f..eaaa6e6a 100644 --- a/www/assets/js/ui.js +++ b/www/assets/js/ui.js @@ -224,10 +224,15 @@ $("#userpl_save").click(function() { }); /* video controls */ +function selectQuality(select, preset) { + +} (function() { function qualHandler(select, preset) { $(select).click(function() { VIDEOQUALITY = preset; + USEROPTS.default_quality = select; + saveOpts(); var btn = $("#qualitywrap .btn.dropdown-toggle"); var caret = btn.find(".caret").detach(); btn.text($(select).text()); @@ -236,11 +241,14 @@ $("#userpl_save").click(function() { PLAYER.player.setPlaybackQuality(VIDEOQUALITY); }); } + qualHandler("#quality_auto", ""); qualHandler("#quality_240p", "small"); qualHandler("#quality_360p", "medium"); qualHandler("#quality_480p", "large"); qualHandler("#quality_720p", "hd720"); qualHandler("#quality_1080p", "hd1080"); + if($(USEROPTS.default_quality).length > 0) + $(USEROPTS.default_quality).click(); })(); $("#mediarefresh").click(function() { diff --git a/www/channel.html b/www/channel.html index 9cee56ee..cdca4f8a 100644 --- a/www/channel.html +++ b/www/channel.html @@ -149,6 +149,7 @@ Quality