aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/server.js74
-rw-r--r--res/client.js16
-rw-r--r--res/langs/en.json1
-rw-r--r--res/langs/ru.json1
-rw-r--r--src/client/Main.hx7
-rw-r--r--src/server/Main.hx49
-rw-r--r--src/server/ServerState.hx13
7 files changed, 147 insertions, 14 deletions
diff --git a/build/server.js b/build/server.js
index ce5bb7b..406d1b4 100644
--- a/build/server.js
+++ b/build/server.js
@@ -647,22 +647,29 @@ var server_Main = function(port,wsPort) {
this.clients = [];
this.rootDir = "" + __dirname + "/..";
var _gthis = this;
+ this.statePath = "" + this.rootDir + "/user/state.json";
this.config = this.getUserConfig();
+ this.loadState();
this.wss = new js_npm_ws_Server({ port : wsPort});
this.wss.on("connection",$bind(this,this.onConnect));
var exit = function() {
+ _gthis.saveState();
process.exit();
};
process.on("exit",exit);
process.on("SIGINT",exit);
process.on("SIGUSR1",exit);
process.on("SIGUSR2",exit);
- process.on("uncaughtException",function(log) {
- haxe_Log.trace(log,{ fileName : "src/server/Main.hx", lineNumber : 49, className : "server.Main", methodName : "new"});
+ process.on("uncaughtException",function(err) {
+ haxe_Log.trace(err,{ fileName : "src/server/Main.hx", lineNumber : 52, className : "server.Main", methodName : "new"});
+ _gthis.logError("uncaughtException",{ message : err.message, stack : err.stack});
+ exit();
return;
});
process.on("unhandledRejection",function(reason,promise) {
- haxe_Log.trace("Unhandled Rejection at:",{ fileName : "src/server/Main.hx", lineNumber : 52, className : "server.Main", methodName : "new", customParams : [reason]});
+ haxe_Log.trace("Unhandled Rejection at:",{ fileName : "src/server/Main.hx", lineNumber : 60, className : "server.Main", methodName : "new", customParams : [reason]});
+ _gthis.logError("unhandledRejection",reason);
+ exit();
return;
});
this.localIp = server_Utils.getLocalIp();
@@ -670,8 +677,8 @@ var server_Main = function(port,wsPort) {
this.port = port;
server_Utils.getGlobalIp(function(ip) {
_gthis.globalIp = ip;
- haxe_Log.trace("Local: http://" + _gthis.localIp + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 60, className : "server.Main", methodName : "new"});
- haxe_Log.trace("Global: http://" + _gthis.globalIp + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 61, className : "server.Main", methodName : "new"});
+ haxe_Log.trace("Local: http://" + _gthis.localIp + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 70, className : "server.Main", methodName : "new"});
+ haxe_Log.trace("Global: http://" + _gthis.globalIp + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 71, className : "server.Main", methodName : "new"});
return;
});
var dir = "" + this.rootDir + "/res";
@@ -700,18 +707,47 @@ server_Main.prototype = {
var field = _g1[_g];
++_g;
if(Reflect.field(config,field) == null) {
- haxe_Log.trace("Warning: config field \"" + field + "\" is unknown",{ fileName : "src/server/Main.hx", lineNumber : 79, className : "server.Main", methodName : "getUserConfig"});
+ haxe_Log.trace("Warning: config field \"" + field + "\" is unknown",{ fileName : "src/server/Main.hx", lineNumber : 89, className : "server.Main", methodName : "getUserConfig"});
}
config[field] = Reflect.field(customConfig,field);
}
return config;
}
+ ,saveState: function() {
+ var json = JSON.stringify({ videoList : this.videoList, messages : this.messages, timer : { time : this.videoTimer.getTime(), paused : this.videoTimer.isPaused()}},null,"\t");
+ js_node_Fs.writeFileSync(this.statePath,json);
+ }
+ ,loadState: function() {
+ if(!sys_FileSystem.exists(this.statePath)) {
+ return;
+ }
+ var data = JSON.parse(js_node_Fs.readFileSync(this.statePath,{ encoding : "utf8"}));
+ this.videoList.length = 0;
+ this.messages.length = 0;
+ var _g = 0;
+ var _g1 = data.videoList;
+ while(_g < _g1.length) this.videoList.push(_g1[_g++]);
+ var _g2 = 0;
+ var _g3 = data.messages;
+ while(_g2 < _g3.length) this.messages.push(_g3[_g2++]);
+ this.videoTimer.start();
+ this.videoTimer.setTime(data.timer.time);
+ this.videoTimer.pause();
+ }
+ ,logError: function(type,data) {
+ var crashesFolder = "" + this.rootDir + "/user/crashes";
+ var name = new Date().toISOString() + "-" + type;
+ if(!sys_FileSystem.exists(crashesFolder)) {
+ sys_FileSystem.createDirectory(crashesFolder);
+ }
+ js_node_Fs.writeFileSync("" + crashesFolder + "/" + name + ".json",JSON.stringify(data,null,"\t"));
+ }
,onConnect: function(ws,req) {
var _gthis = this;
var ip = req.connection.remoteAddress;
var id = this.freeIds.length > 0 ? this.freeIds.shift() : this.clients.length;
var name = "Guest " + (id + 1);
- haxe_Log.trace("" + name + " connected (" + ip + ")",{ fileName : "src/server/Main.hx", lineNumber : 89, className : "server.Main", methodName : "onConnect"});
+ haxe_Log.trace("" + name + " connected (" + ip + ")",{ fileName : "src/server/Main.hx", lineNumber : 131, className : "server.Main", methodName : "onConnect"});
var client = new Client(ws,req,id,name,0);
if(req.connection.localAddress == ip) {
client.group |= 4;
@@ -737,7 +773,7 @@ server_Main.prototype = {
return;
});
ws.on("close",function(err) {
- haxe_Log.trace("Client " + client.name + " disconnected",{ fileName : "src/server/Main.hx", lineNumber : 117, className : "server.Main", methodName : "onConnect"});
+ haxe_Log.trace("Client " + client.name + " disconnected",{ fileName : "src/server/Main.hx", lineNumber : 159, className : "server.Main", methodName : "onConnect"});
server_Utils.sortedPush(_gthis.freeIds,client.id);
HxOverrides.remove(_gthis.clients,client);
_gthis.sendClientList();
@@ -774,6 +810,7 @@ server_Main.prototype = {
}
break;
case "ClearChat":
+ this.messages.length = 0;
if((client.group & 4) != 0) {
this.broadcast(data);
}
@@ -1087,6 +1124,27 @@ sys_FileSystem.exists = function(path) {
return false;
}
};
+sys_FileSystem.createDirectory = function(path) {
+ try {
+ js_node_Fs.mkdirSync(path);
+ } catch( e ) {
+ var e1 = ((e) instanceof js__$Boot_HaxeError) ? e.val : e;
+ if(e1.code == "ENOENT") {
+ sys_FileSystem.createDirectory(js_node_Path.dirname(path));
+ js_node_Fs.mkdirSync(path);
+ } else {
+ var stat;
+ try {
+ stat = js_node_Fs.statSync(path);
+ } catch( _ ) {
+ throw e1;
+ }
+ if(!stat.isDirectory()) {
+ throw e1;
+ }
+ }
+ }
+};
function $getIterator(o) { if( o instanceof Array ) return HxOverrides.iter(o); else return o.iterator(); }
function $bind(o,m) { if( m == null ) return null; if( m.__id__ == null ) m.__id__ = $global.$haxeUID++; var f; if( o.hx__closures__ == null ) o.hx__closures__ = {}; else f = o.hx__closures__[m.__id__]; if( f == null ) { f = m.bind(o); o.hx__closures__[m.__id__] = f; } return f; }
$global.$haxeUID |= 0;
diff --git a/res/client.js b/res/client.js
index d9cc67b..4c3a8a5 100644
--- a/res/client.js
+++ b/res/client.js
@@ -591,6 +591,11 @@ client_Main.prototype = {
name = Lang.get("rawVideo");
}
this.getRemoteVideoDuration(url,function(duration) {
+ if(duration == 0) {
+ var tmp = Lang.get("addVideoError");
+ _gthis.serverMessage(4,tmp);
+ return;
+ }
_gthis.send({ type : "AddVideo", addVideo : { item : { url : url, title : name, author : _gthis.personal.name, duration : duration}, atEnd : atEnd}});
callback();
return;
@@ -606,7 +611,7 @@ client_Main.prototype = {
while(_g1 < items.length) _g.push(items[_g1++].url);
return _g;
}
- ,replaceLocalIp: function(url) {
+ ,tryLocalIp: function(url) {
if(this.host == this.globalIp) {
return url;
}
@@ -617,6 +622,9 @@ client_Main.prototype = {
var video = window.document.createElement("video");
video.src = src;
video.onerror = function(e) {
+ if(player.contains(video)) {
+ player.removeChild(video);
+ }
callback(0);
return;
};
@@ -633,7 +641,7 @@ client_Main.prototype = {
var data = JSON.parse(e.data);
var t = data.type;
var t1 = t.charAt(0).toLowerCase() + HxOverrides.substr(t,1,null);
- haxe_Log.trace("Event: " + data.type,{ fileName : "src/client/Main.hx", lineNumber : 188, className : "client.Main", methodName : "onMessage", customParams : [data[t1]]});
+ haxe_Log.trace("Event: " + data.type,{ fileName : "src/client/Main.hx", lineNumber : 193, className : "client.Main", methodName : "onMessage", customParams : [data[t1]]});
switch(data.type) {
case "AddVideo":
if(this.player.isListEmpty()) {
@@ -758,6 +766,8 @@ client_Main.prototype = {
if(guestName.value.length > 0) {
this.send({ type : "Login", login : { clientName : guestName.value}});
}
+ window.document.querySelector("#messagebuffer").innerHTML = "";
+ this.serverMessage(1);
var _g = 0;
var _g1 = connected.history;
while(_g < _g1.length) {
@@ -997,7 +1007,7 @@ client_Player.prototype = {
this.isLoaded = false;
this.video = window.document.createElement("video");
this.video.id = "videoplayer";
- item.url = this.main.replaceLocalIp(item.url);
+ item.url = this.main.tryLocalIp(item.url);
this.video.src = item.url;
this.video.controls = true;
this.video.oncanplaythrough = function(e) {
diff --git a/res/langs/en.json b/res/langs/en.json
index 5fa5d53..ea1ca74 100644
--- a/res/langs/en.json
+++ b/res/langs/en.json
@@ -6,6 +6,7 @@
"online": "online",
"nothingPlaying": "Nothing Playing",
"usernameError": "Username must be from 1 to $MAX characters and don't repeat another's.",
+ "addVideoError": "Failed to add video.",
"rawVideo": "Raw video",
"videos": "videos",
"addedBy": "Added by",
diff --git a/res/langs/ru.json b/res/langs/ru.json
index f9944d1..eb2c1b3 100644
--- a/res/langs/ru.json
+++ b/res/langs/ru.json
@@ -6,6 +6,7 @@
"online": "онлайн",
"nothingPlaying": "Ничего не играет",
"usernameError": "Ник должен быть от 1 до $MAX символов и не повторять чужие.",
+ "addVideoError": "Не удалось добавить видео.",
"rawVideo": "Исходное видео",
"videos": "видео",
"addedBy": "Добавлено",
diff --git a/src/client/Main.hx b/src/client/Main.hx
index 432a095..aa0a648 100644
--- a/src/client/Main.hx
+++ b/src/client/Main.hx
@@ -136,6 +136,10 @@ class Main {
else name = Lang.get("rawVideo");
getRemoteVideoDuration(url, (duration:Float) -> {
+ if (duration == 0) {
+ serverMessage(4, Lang.get("addVideoError"));
+ return;
+ }
send({
type: AddVideo, addVideo: {
item: {
@@ -172,6 +176,7 @@ class Main {
video.src = src;
// TODO catch errors on AddVideo and getRemoteVideoDuration
video.onerror = e -> {
+ if (player.contains(video)) player.removeChild(video);
callback(0);
}
video.onloadedmetadata = () -> {
@@ -294,6 +299,8 @@ class Main {
clientName: guestName.value
}
});
+ ge("#messagebuffer").innerHTML = "";
+ serverMessage(1);
for (message in connected.history) {
addMessage(message.name, message.text, message.time);
}
diff --git a/src/server/Main.hx b/src/server/Main.hx
index 442abc8..9a245fe 100644
--- a/src/server/Main.hx
+++ b/src/server/Main.hx
@@ -20,6 +20,7 @@ using Lambda;
class Main {
final rootDir = '$__dirname/..';
+ final statePath:String;
final wss:WSServer;
final localIp:String;
var globalIp:String;
@@ -34,22 +35,31 @@ class Main {
static function main():Void new Main();
public function new(port = 4200, wsPort = 4201) {
+ statePath = '$rootDir/user/state.json';
config = getUserConfig();
+ loadState();
wss = new WSServer({port: wsPort});
wss.on("connection", onConnect);
function exit() {
- // TODO save state
+ saveState();
process.exit();
}
process.on("exit", exit);
process.on("SIGINT", exit); // ctrl+c
process.on("SIGUSR1", exit); // kill pid
process.on("SIGUSR2", exit);
- process.on("uncaughtException", (log) -> {
- trace(log);
+ process.on("uncaughtException", err -> {
+ trace(err);
+ logError("uncaughtException", {
+ message: err.message,
+ stack: err.stack
+ });
+ exit();
});
process.on("unhandledRejection", (reason, promise) -> {
trace("Unhandled Rejection at:", reason);
+ logError("unhandledRejection", reason);
+ exit();
});
localIp = Utils.getLocalIp();
globalIp = localIp;
@@ -82,6 +92,38 @@ class Main {
return config;
}
+ function saveState():Void {
+ final data:ServerState = {
+ videoList: videoList,
+ messages: messages,
+ timer: {
+ time: videoTimer.getTime(),
+ paused: videoTimer.isPaused()
+ }
+ }
+ final json = Json.stringify(data, "\t");
+ File.saveContent(statePath, json);
+ }
+
+ function loadState():Void {
+ if (!FileSystem.exists(statePath)) return;
+ final data:ServerState = Json.parse(File.getContent(statePath));
+ videoList.resize(0);
+ messages.resize(0);
+ for (item in data.videoList) videoList.push(item);
+ for (message in data.messages) messages.push(message);
+ videoTimer.start();
+ videoTimer.setTime(data.timer.time);
+ videoTimer.pause();
+ }
+
+ function logError(type:String, data:Dynamic):Void {
+ final crashesFolder = '$rootDir/user/crashes';
+ final name = new Date().toISOString() + "-" + type;
+ if (!FileSystem.exists(crashesFolder)) FileSystem.createDirectory(crashesFolder);
+ File.saveContent('$crashesFolder/$name.json', Json.stringify(data, "\t"));
+ }
+
function onConnect(ws:WebSocket, req:IncomingMessage):Void {
final ip = req.connection.remoteAddress;
final id = freeIds.length > 0 ? freeIds.shift() : clients.length;
@@ -267,6 +309,7 @@ class Main {
}
case ClearChat:
+ messages.resize(0);
if (client.isAdmin) broadcast(data);
case ClearPlaylist:
diff --git a/src/server/ServerState.hx b/src/server/ServerState.hx
new file mode 100644
index 0000000..06240e5
--- /dev/null
+++ b/src/server/ServerState.hx
@@ -0,0 +1,13 @@
+package server;
+
+import Types.Message;
+import Types.VideoItem;
+
+typedef ServerState = {
+ videoList:Array<VideoItem>,
+ messages:Array<Message>,
+ timer:{
+ time:Float,
+ paused:Bool
+ }
+}
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage