aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/server.js103
-rw-r--r--res/client.js360
-rw-r--r--res/index.html2
-rw-r--r--src/Types.hx8
-rw-r--r--src/client/IPlayer.hx12
-rw-r--r--src/client/Main.hx45
-rw-r--r--src/client/Player.hx218
-rw-r--r--src/client/players/Raw.hx72
-rw-r--r--src/server/Main.hx64
-rw-r--r--src/server/ServerState.hx1
10 files changed, 625 insertions, 260 deletions
diff --git a/build/server.js b/build/server.js
index a6edc55..4cd1089 100644
--- a/build/server.js
+++ b/build/server.js
@@ -176,6 +176,13 @@ HxOverrides.iter = function(a) {
};
var Lambda = function() { };
Lambda.__name__ = true;
+Lambda.exists = function(it,f) {
+ var x = $getIterator(it);
+ while(x.hasNext()) if(f(x.next())) {
+ return true;
+ }
+ return false;
+};
Lambda.find = function(it,f) {
var v = $getIterator(it);
while(v.hasNext()) {
@@ -638,6 +645,7 @@ var server_Main = function(port,wsPort) {
}
this.loadedClientsCount = 0;
this.htmlChars = new EReg("[&^<>'\"]","");
+ this.itemPos = 0;
this.messages = [];
this.videoTimer = new server_VideoTimer();
this.videoList = [];
@@ -676,8 +684,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 : 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"});
+ haxe_Log.trace("Local: http://" + _gthis.localIp + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 71, className : "server.Main", methodName : "new"});
+ haxe_Log.trace("Global: http://" + _gthis.globalIp + ":" + port,{ fileName : "src/server/Main.hx", lineNumber : 72, className : "server.Main", methodName : "new"});
return;
});
var dir = "" + this.rootDir + "/res";
@@ -709,28 +717,29 @@ 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 : 92, className : "server.Main", methodName : "getUserConfig"});
+ haxe_Log.trace("Warning: config field \"" + field + "\" is unknown",{ fileName : "src/server/Main.hx", lineNumber : 93, className : "server.Main", methodName : "getUserConfig"});
}
config[field] = Reflect.field(customConfig,field);
}
return config;
}
,saveState: function() {
- haxe_Log.trace("Saving state...",{ fileName : "src/server/Main.hx", lineNumber : 99, className : "server.Main", methodName : "saveState"});
- var json = JSON.stringify({ videoList : this.videoList, messages : this.messages, timer : { time : this.videoTimer.getTime(), paused : this.videoTimer.isPaused()}},null,"\t");
+ haxe_Log.trace("Saving state...",{ fileName : "src/server/Main.hx", lineNumber : 100, className : "server.Main", methodName : "saveState"});
+ var json = JSON.stringify({ videoList : this.videoList, itemPos : this.itemPos, 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;
}
- haxe_Log.trace("Loading state...",{ fileName : "src/server/Main.hx", lineNumber : 114, className : "server.Main", methodName : "loadState"});
+ haxe_Log.trace("Loading state...",{ fileName : "src/server/Main.hx", lineNumber : 116, className : "server.Main", methodName : "loadState"});
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++]);
+ this.itemPos = data.itemPos;
var _g2 = 0;
var _g3 = data.messages;
while(_g2 < _g3.length) this.messages.push(_g3[_g2++]);
@@ -739,7 +748,7 @@ server_Main.prototype = {
this.videoTimer.pause();
}
,logError: function(type,data) {
- haxe_Log.trace(type,{ fileName : "src/server/Main.hx", lineNumber : 126, className : "server.Main", methodName : "logError", customParams : [data]});
+ haxe_Log.trace(type,{ fileName : "src/server/Main.hx", lineNumber : 129, className : "server.Main", methodName : "logError", customParams : [data]});
var crashesFolder = "" + this.rootDir + "/user/crashes";
var name = new Date().toISOString() + "-" + type;
if(!sys_FileSystem.exists(crashesFolder)) {
@@ -755,7 +764,7 @@ server_Main.prototype = {
return;
}
var url = "http://" + process.env["APP_URL"];
- haxe_Log.trace("Ping " + url,{ fileName : "src/server/Main.hx", lineNumber : 140, className : "server.Main", methodName : "initIntergationHandlers"});
+ haxe_Log.trace("Ping " + url,{ fileName : "src/server/Main.hx", lineNumber : 143, className : "server.Main", methodName : "initIntergationHandlers"});
js_node_Http.get(url,function(r) {
return;
});
@@ -767,7 +776,7 @@ server_Main.prototype = {
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 : 150, className : "server.Main", methodName : "onConnect"});
+ haxe_Log.trace("" + name + " connected (" + ip + ")",{ fileName : "src/server/Main.hx", lineNumber : 153, className : "server.Main", methodName : "onConnect"});
var client = new Client(ws,req,id,name,0);
if(req.connection.localAddress == ip) {
client.group |= 4;
@@ -785,7 +794,7 @@ server_Main.prototype = {
var _g1 = 0;
var _g2 = this.clients;
while(_g1 < _g2.length) _g.push(_g2[_g1++].getData());
- this.send(client,{ type : "Connected", connected : { config : tmp, history : tmp1, isUnknownClient : true, clientName : client1, clients : _g, videoList : this.videoList, globalIp : this.globalIp}});
+ this.send(client,{ type : "Connected", connected : { config : tmp, history : tmp1, isUnknownClient : true, clientName : client1, clients : _g, videoList : this.videoList, itemPos : this.itemPos, globalIp : this.globalIp}});
this.sendClientList();
ws.on("message",function(data) {
var tmp2 = JSON.parse(data);
@@ -793,7 +802,7 @@ server_Main.prototype = {
return;
});
ws.on("close",function(err) {
- haxe_Log.trace("Client " + client.name + " disconnected",{ fileName : "src/server/Main.hx", lineNumber : 178, className : "server.Main", methodName : "onConnect"});
+ haxe_Log.trace("Client " + client.name + " disconnected",{ fileName : "src/server/Main.hx", lineNumber : 182, className : "server.Main", methodName : "onConnect"});
server_Utils.sortedPush(_gthis.freeIds,client.id);
HxOverrides.remove(_gthis.clients,client);
_gthis.sendClientList();
@@ -815,15 +824,20 @@ server_Main.prototype = {
switch(data.type) {
case "AddVideo":
var item = data.addVideo.item;
+ var local = "" + this.localIp + ":" + this.port;
+ if(item.url.indexOf(local) != -1) {
+ item.url = StringTools.replace(item.url,local,"" + this.globalIp + ":" + this.port);
+ }
item.author = client.name;
- var localOrigin = "" + this.localIp + ":" + this.port;
- if(item.url.indexOf(localOrigin) != -1) {
- item.url = StringTools.replace(item.url,localOrigin,"" + this.globalIp + ":" + this.port);
+ if(Lambda.exists(this.videoList,function(i) {
+ return i.url == item.url;
+ })) {
+ return;
}
if(data.addVideo.atEnd) {
this.videoList.push(item);
} else {
- this.videoList.splice(1,0,item);
+ this.videoList.splice(this.itemPos + 1,0,item);
}
this.broadcast(data);
if(this.videoList.length == 1) {
@@ -839,6 +853,7 @@ server_Main.prototype = {
case "ClearPlaylist":
this.videoTimer.stop();
this.videoList.length = 0;
+ this.itemPos = 0;
this.broadcast(data);
break;
case "Connected":
@@ -847,9 +862,9 @@ server_Main.prototype = {
if(this.videoList.length == 0) {
return;
}
- if(this.videoTimer.getTime() > this.videoList[0].duration) {
+ if(this.videoTimer.getTime() > this.videoList[this.itemPos].duration) {
this.videoTimer.stop();
- this.onMessage(client,{ type : "RemoveVideo", removeVideo : { url : this.videoList[0].url}});
+ this.onMessage(client,{ type : "SkipVideo", skipVideo : { url : this.videoList[this.itemPos].url}});
return;
}
this.send(client,{ type : "GetTime", getTime : { time : this.videoTimer.getTime(), paused : this.videoTimer.isPaused()}});
@@ -916,15 +931,26 @@ server_Main.prototype = {
return;
}
var url = data.removeVideo.url;
- var isFirst = this.videoList[0].url == url;
- if(isFirst) {
- this.videoTimer.stop();
+ var item1 = Lambda.find(this.videoList,function(item2) {
+ return item2.url == url;
+ });
+ if(item1 == null) {
+ return;
}
- HxOverrides.remove(this.videoList,Lambda.find(this.videoList,function(item1) {
- return item1.url == url;
- }));
- if(isFirst && this.videoList.length > 0) {
- this.restartWaitTimer();
+ var index = this.videoList.indexOf(item1);
+ var isCurrent = this.videoList[this.itemPos].url == url;
+ if(index < this.itemPos) {
+ this.itemPos--;
+ }
+ HxOverrides.remove(this.videoList,item1);
+ if(isCurrent) {
+ if(this.itemPos >= this.videoList.length) {
+ this.itemPos = 0;
+ }
+ this.videoTimer.stop();
+ if(this.videoList.length > 0) {
+ this.restartWaitTimer();
+ }
}
this.broadcast(data);
break;
@@ -966,11 +992,34 @@ server_Main.prototype = {
if(this.videoList.length == 0) {
return;
}
- var first = this.videoList.shift();
+ var current = this.videoList[this.itemPos];
+ HxOverrides.remove(this.videoList,current);
server_Utils.shuffle(this.videoList);
- this.videoList.unshift(first);
+ this.videoList.splice(this.itemPos,0,current);
this.broadcast({ type : "UpdatePlaylist", updatePlaylist : { videoList : this.videoList}});
break;
+ case "SkipVideo":
+ if(this.videoList.length == 0) {
+ return;
+ }
+ var item3 = this.videoList[this.itemPos];
+ if(item3.url != data.skipVideo.url) {
+ return;
+ }
+ if(!item3.isTemp) {
+ this.itemPos++;
+ } else {
+ HxOverrides.remove(this.videoList,item3);
+ }
+ if(this.itemPos >= this.videoList.length) {
+ this.itemPos = 0;
+ }
+ this.videoTimer.stop();
+ if(this.videoList.length > 0) {
+ this.restartWaitTimer();
+ }
+ this.broadcast(data);
+ break;
case "UpdateClients":
this.sendClientList();
break;
diff --git a/res/client.js b/res/client.js
index 0f003eb..bcbe9cd 100644
--- a/res/client.js
+++ b/res/client.js
@@ -674,23 +674,30 @@ client_Main.prototype = {
_gthis.send({ type : "SetLeader", setLeader : { clientName : (_gthis.personal.group & 2) != 0 ? "" : _gthis.personal.name}});
return;
};
- window.document.querySelector("#queue_next").onclick = function(e1) {
+ window.document.querySelector("#voteskip").onclick = function(e1) {
+ if(_gthis.player.isListEmpty()) {
+ return;
+ }
+ _gthis.send({ type : "SkipVideo", skipVideo : { url : _gthis.player.getItems()[_gthis.player.getItemPos()].url}});
+ return;
+ };
+ window.document.querySelector("#queue_next").onclick = function(e2) {
_gthis.addVideoUrl(false);
return;
};
- window.document.querySelector("#queue_end").onclick = function(e2) {
+ window.document.querySelector("#queue_end").onclick = function(e3) {
_gthis.addVideoUrl(true);
return;
};
- window.document.querySelector("#mediaurl").onkeydown = function(e3) {
- if(e3.keyCode == 13) {
+ window.document.querySelector("#mediaurl").onkeydown = function(e4) {
+ if(e4.keyCode == 13) {
_gthis.addVideoUrl(true);
}
};
}
,addVideoUrl: function(atEnd) {
- var _gthis = this;
var mediaUrl = window.document.querySelector("#mediaurl");
+ var isTemp = window.document.querySelector("#addfromurl").querySelector(".add-temp").checked;
var url = mediaUrl.value;
if(url.length == 0) {
return;
@@ -698,22 +705,29 @@ client_Main.prototype = {
mediaUrl.value = "";
var _this_r = new RegExp(",(https?)","g".split("u").join(""));
var links = url.replace(_this_r,"|$1").split("|");
- this.addVideo(atEnd || this.player.isListEmpty() ? links.shift() : links.pop(),atEnd,function() {
- _gthis.addVideoArray(links,atEnd);
- return;
- });
+ if(!atEnd) {
+ var first = null;
+ if(this.player.isListEmpty()) {
+ first = links.shift();
+ }
+ links.reverse();
+ if(this.player.isListEmpty()) {
+ links.unshift(first);
+ }
+ }
+ this.addVideoArray(links,atEnd,isTemp);
}
- ,addVideoArray: function(links,atEnd) {
+ ,addVideoArray: function(links,atEnd,isTemp) {
var _gthis = this;
if(links.length == 0) {
return;
}
- this.addVideo(atEnd ? links.shift() : links.pop(),atEnd,function() {
- _gthis.addVideoArray(links,atEnd);
+ this.addVideo(links.shift(),atEnd,isTemp,function() {
+ _gthis.addVideoArray(links,atEnd,isTemp);
return;
});
}
- ,addVideo: function(url,atEnd,callback) {
+ ,addVideo: function(url,atEnd,isTemp,callback) {
var _gthis = this;
var protocol = window.location.protocol;
if(StringTools.startsWith(url,"/")) {
@@ -722,7 +736,8 @@ client_Main.prototype = {
if(!StringTools.startsWith(url,"http")) {
url = "" + protocol + "//" + url;
}
- var name = HxOverrides.substr(url,url.lastIndexOf("/") + 1,null);
+ var pos = url.lastIndexOf("/") + 1;
+ var name = HxOverrides.substr(url,pos,null);
var matchName = new EReg("^(.+)\\.","");
if(matchName.match(name)) {
name = matchName.matched(1);
@@ -735,7 +750,7 @@ client_Main.prototype = {
_gthis.serverMessage(4,tmp);
return;
}
- _gthis.send({ type : "AddVideo", addVideo : { item : { url : url, title : name, author : _gthis.personal.name, duration : duration}, atEnd : atEnd}});
+ _gthis.send({ type : "AddVideo", addVideo : { item : { url : url, title : name, author : _gthis.personal.name, duration : duration, isTemp : isTemp}, atEnd : atEnd}});
callback();
return;
});
@@ -744,7 +759,7 @@ client_Main.prototype = {
if(this.player.hasVideo()) {
this.player.removeVideo();
} else if(!this.player.isListEmpty()) {
- this.player.setVideo(this.player.getItems()[0]);
+ this.player.setVideo(this.player.getItemPos());
}
return this.player.hasVideo();
}
@@ -788,13 +803,13 @@ 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 : 207, className : "client.Main", methodName : "onMessage", customParams : [data[t1]]});
+ haxe_Log.trace("Event: " + data.type,{ fileName : "src/client/Main.hx", lineNumber : 228, className : "client.Main", methodName : "onMessage", customParams : [data[t1]]});
switch(data.type) {
case "AddVideo":
- if(this.player.isListEmpty()) {
- this.player.setVideo(data.addVideo.item);
- }
this.player.addVideoItem(data.addVideo.item,data.addVideo.atEnd);
+ if(this.player.itemsLength() == 1) {
+ this.player.setVideo(0);
+ }
break;
case "ClearChat":
this.clearChat();
@@ -885,6 +900,12 @@ client_Main.prototype = {
break;
case "ShufflePlaylist":
break;
+ case "SkipVideo":
+ this.player.skipItem(data.skipVideo.url);
+ if(this.player.isListEmpty()) {
+ this.player.pause();
+ }
+ break;
case "UpdateClients":
this.updateClients(data.updateClients.clients);
this.personal = ClientTools.getByName(this.clients,this.personal.name,this.personal);
@@ -922,7 +943,7 @@ client_Main.prototype = {
++_g;
this.addMessage(message.name,message.text,message.time);
}
- this.player.setItems(connected.videoList);
+ this.player.setItems(connected.videoList,connected.itemPos);
}
,setConfig: function(config) {
this.config = config;
@@ -946,8 +967,9 @@ client_Main.prototype = {
}
var smilesWrap = window.document.querySelector("#smileswrap");
smilesWrap.onclick = function(e) {
+ var el = e.target;
var form = window.document.querySelector("#chatline");
- form.value += " " + e.target.title;
+ form.value += " " + el.title;
form.focus();
return;
};
@@ -1064,6 +1086,7 @@ client_Main.prototype = {
nameDiv.className = "username";
nameDiv.textContent = name + ": ";
var textDiv = window.document.createElement("span");
+ text = StringTools.htmlEscape(text);
if(StringTools.startsWith(text,"/")) {
if(name == this.personal.name) {
this.handleCommands(HxOverrides.substr(text,1,null));
@@ -1077,7 +1100,6 @@ client_Main.prototype = {
text = text.replace(filter.regex.r,filter.replace);
}
}
- text = StringTools.htmlEscape(text);
textDiv.innerHTML = text;
var isInChatEnd = msgBuf.scrollHeight - msgBuf.scrollTop == msgBuf.clientHeight;
userDiv.appendChild(tstamp);
@@ -1150,93 +1172,105 @@ client_MobileView.init = function() {
};
};
var client_Player = function(main) {
+ this.matchYoutube = new EReg("v=([A-z0-9_-]+)","");
this.skipSetTime = false;
this.isLoaded = false;
- this.player = window.document.querySelector("#ytapiplayer");
+ this.itemPos = 0;
+ this.currentSrc = "";
+ this.playerEl = window.document.querySelector("#ytapiplayer");
this.videoItemsEl = window.document.querySelector("#queue");
this.items = [];
this.main = main;
};
client_Player.__name__ = true;
client_Player.prototype = {
- setVideo: function(item) {
- var _gthis = this;
+ setPlayer: function(player) {
+ this.player = player;
+ }
+ ,isYoutube: function(url) {
+ if(url.indexOf("youtube.com/") == -1) {
+ return false;
+ }
+ if(url.indexOf("youtu.be/") == -1) {
+ return false;
+ }
+ if(!this.matchYoutube.match(url)) {
+ return false;
+ }
+ return true;
+ }
+ ,setVideo: function(i) {
+ var item = this.items[i];
+ if(!this.isYoutube(item.url)) {
+ this.setPlayer(new client_players_Raw(this.main,this));
+ }
+ var childs = this.videoItemsEl.children;
+ if(childs[this.itemPos] != null) {
+ childs[this.itemPos].classList.remove("queue_active");
+ }
+ this.itemPos = i;
+ childs[this.itemPos].classList.add("queue_active");
+ this.currentSrc = item.url;
+ this.playerEl.textContent = "";
this.isLoaded = false;
- this.video = window.document.createElement("video");
- this.video.id = "videoplayer";
- item.url = this.main.tryLocalIp(item.url);
- this.video.src = item.url;
- this.video.controls = true;
- var isTouch = 'ontouchstart' in window;
- if(!isTouch) {
- haxe_Timer.delay(function() {
- _gthis.video.controls = false;
- _gthis.video.onmouseover = function(e) {
- _gthis.video.controls = true;
- _gthis.video.onmouseover = null;
- return _gthis.video.onmousemove = null;
- };
- return _gthis.video.onmousemove = _gthis.video.onmouseover;
- },3000);
+ this.player.loadVideo(item);
+ window.document.querySelector("#currenttitle").textContent = item.title;
+ }
+ ,removeVideo: function() {
+ this.currentSrc = "";
+ this.player.removeVideo();
+ window.document.querySelector("#currenttitle").textContent = Lang.get("nothingPlaying");
+ }
+ ,onCanBePlayed: function() {
+ if(!this.isLoaded) {
+ this.main.send({ type : "VideoLoaded"});
}
- this.video.oncanplaythrough = function(e1) {
- if(!_gthis.isLoaded) {
- _gthis.main.send({ type : "VideoLoaded"});
- }
- return _gthis.isLoaded = true;
- };
- this.video.onseeking = function(e2) {
- if(_gthis.skipSetTime) {
- _gthis.skipSetTime = false;
- return;
- }
- if((_gthis.main.personal.group & 1 << ClientGroup.Leader._hx_index) == 0) {
- return;
- }
- _gthis.main.send({ type : "SetTime", setTime : { time : _gthis.video.currentTime}});
+ this.isLoaded = true;
+ }
+ ,onPlay: function() {
+ if((this.main.personal.group & 2) == 0) {
return;
- };
- this.video.onpause = function(e3) {
- if((_gthis.main.personal.group & 1 << ClientGroup.Leader._hx_index) == 0) {
- return;
- }
- _gthis.main.send({ type : "Pause", pause : { time : _gthis.video.currentTime}});
+ }
+ this.main.send({ type : "Play", play : { time : this.getTime()}});
+ }
+ ,onPause: function() {
+ if((this.main.personal.group & 2) == 0) {
return;
- };
- this.video.onplay = function(e4) {
- if((_gthis.main.personal.group & 1 << ClientGroup.Leader._hx_index) == 0) {
- return;
- }
- _gthis.main.send({ type : "Play", play : { time : _gthis.video.currentTime}});
+ }
+ this.main.send({ type : "Pause", pause : { time : this.getTime()}});
+ }
+ ,onSetTime: function() {
+ if(this.skipSetTime) {
+ this.skipSetTime = false;
return;
- };
- this.player.textContent = "";
- this.player.appendChild(this.video);
- window.document.querySelector("#currenttitle").textContent = item.title;
+ }
+ if((this.main.personal.group & 2) == 0) {
+ return;
+ }
+ this.main.send({ type : "SetTime", setTime : { time : this.getTime()}});
}
,addVideoItem: function(item,atEnd) {
var _gthis = this;
- this.items.push(item);
- var itemEl = this.nodeFromString("<li class=\"queue_entry pluid-0 queue_temp queue_active\" title=\"" + Lang.get("addedBy") + ": " + item.author + "\">\n\t\t\t\t<a class=\"qe_title\" href=\"" + item.url + "\" target=\"_blank\">" + StringTools.htmlEscape(item.title) + "</a>\n\t\t\t\t<span class=\"qe_time\">" + this.duration(item.duration) + "</span>\n\t\t\t\t<div class=\"qe_clear\"></div>\n\t\t\t\t<div class=\"btn-group\" style=\"display: inline-block;\">\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-play\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-play\"></span>" + Lang.get("play") + "\n\t\t\t\t\t</button>\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-next\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-share-alt\"></span>" + Lang.get("skip") + "\n\t\t\t\t\t</button>\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-tmp\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-flag\"></span>" + Lang.get("makePermanent") + "\n\t\t\t\t\t</button>\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-delete\" id=\"btn-delete\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-trash\"></span>" + Lang.get("delete") + "\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</li>");
+ var itemEl = this.nodeFromString("<li class=\"queue_entry pluid-0\" title=\"" + Lang.get("addedBy") + ": " + item.author + "\">\n\t\t\t\t<a class=\"qe_title\" href=\"" + item.url + "\" target=\"_blank\">" + StringTools.htmlEscape(item.title) + "</a>\n\t\t\t\t<span class=\"qe_time\">" + this.duration(item.duration) + "</span>\n\t\t\t\t<div class=\"qe_clear\"></div>\n\t\t\t\t<div class=\"btn-group\" style=\"display: inline-block;\">\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-play\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-play\"></span>" + Lang.get("play") + "\n\t\t\t\t\t</button>\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-next\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-share-alt\"></span>" + Lang.get("skip") + "\n\t\t\t\t\t</button>\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-tmp\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-flag\"></span>" + Lang.get("makePermanent") + "\n\t\t\t\t\t</button>\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-delete\" id=\"btn-delete\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-trash\"></span>" + Lang.get("delete") + "\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</li>");
+ if(item.isTemp) {
+ itemEl.classList.add("queue_temp");
+ }
itemEl.querySelector("#btn-delete").onclick = function(e) {
_gthis.main.send({ type : "RemoveVideo", removeVideo : { url : itemEl.querySelector(".qe_title").getAttribute("href")}});
return;
};
if(atEnd) {
+ this.items.push(item);
+ } else {
+ this.items.splice(this.itemPos + 1,0,item);
+ }
+ if(atEnd) {
this.videoItemsEl.appendChild(itemEl);
} else {
- client_Utils.insertAtIndex(this.videoItemsEl,itemEl,1);
+ client_Utils.insertAtIndex(this.videoItemsEl,itemEl,this.itemPos + 1);
}
this.updateCounters();
}
- ,removeVideo: function() {
- if(this.video == null) {
- return;
- }
- this.player.removeChild(this.video);
- this.video = null;
- window.document.querySelector("#currenttitle").textContent = Lang.get("nothingPlaying");
- }
,removeItem: function(url) {
var _g = 0;
var _g1 = this.videoItemsEl.children;
@@ -1248,18 +1282,46 @@ client_Player.prototype = {
break;
}
}
- HxOverrides.remove(this.items,Lambda.find(this.items,function(item) {
- return item.url == url;
- }));
+ var item = Lambda.find(this.items,function(item1) {
+ return item1.url == url;
+ });
+ if(item == null) {
+ return;
+ }
+ var index = this.items.indexOf(item);
+ HxOverrides.remove(this.items,item);
this.updateCounters();
- if(this.video == null) {
+ if(index < this.itemPos) {
+ this.itemPos--;
return;
}
- if(this.video.src == url) {
- if(this.items.length > 0) {
- this.setVideo(this.items[0]);
- }
+ if(index != this.itemPos) {
+ return;
+ }
+ if(this.items.length == 0) {
+ return;
+ }
+ if(this.items[index] == null) {
+ index = 0;
+ }
+ this.setVideo(index);
+ }
+ ,skipItem: function(url) {
+ var item = Lambda.find(this.items,function(item1) {
+ return item1.url == url;
+ });
+ if(item == null) {
+ return;
+ }
+ if(item.isTemp) {
+ this.removeItem(url);
+ return;
}
+ var index = this.items.indexOf(item) + 1;
+ if(index >= this.items.length) {
+ index = 0;
+ }
+ this.setVideo(index);
}
,updateCounters: function() {
var tmp = "" + this.items.length + " ";
@@ -1270,16 +1332,19 @@ client_Player.prototype = {
,getItems: function() {
return this.items;
}
- ,setItems: function(list) {
+ ,setItems: function(list,pos) {
this.clearItems();
+ if(pos != null) {
+ this.itemPos = pos;
+ }
if(list.length == 0) {
return;
}
- if(this.video == null || this.video.src != list[0].url) {
- this.setVideo(list[0]);
- }
var _g = 0;
while(_g < list.length) this.addVideoItem(list[_g++],true);
+ if(this.currentSrc != this.items[this.itemPos].url) {
+ this.setVideo(this.itemPos);
+ }
}
,clearItems: function() {
this.items.length = 0;
@@ -1291,7 +1356,7 @@ client_Player.prototype = {
return;
}
this.removeVideo();
- this.setVideo(this.items[0]);
+ this.setVideo(this.itemPos);
}
,duration: function(time) {
var h = time / 60 / 60 | 0;
@@ -1325,36 +1390,42 @@ client_Player.prototype = {
,isListEmpty: function() {
return this.items.length == 0;
}
+ ,itemsLength: function() {
+ return this.items.length;
+ }
+ ,getItemPos: function() {
+ return this.itemPos;
+ }
,hasVideo: function() {
- return this.video != null;
+ return this.player != null;
}
- ,pause: function() {
- if(this.video == null) {
+ ,play: function() {
+ if(this.player == null) {
return;
}
- this.video.pause();
+ this.player.play();
}
- ,play: function() {
- if(this.video == null) {
+ ,pause: function() {
+ if(this.player == null) {
return;
}
- this.video.play();
+ this.player.pause();
+ }
+ ,getTime: function() {
+ if(this.player == null) {
+ return 0;
+ }
+ return this.player.getTime();
}
,setTime: function(time,isLocal) {
if(isLocal == null) {
isLocal = true;
}
- if(this.video == null) {
+ if(this.player == null) {
return;
}
this.skipSetTime = isLocal;
- this.video.currentTime = time;
- }
- ,getTime: function() {
- if(this.video == null) {
- return 0;
- }
- return this.video.currentTime;
+ this.player.setTime(time);
}
};
var client_Utils = function() { };
@@ -1414,6 +1485,71 @@ client_Utils.copyToClipboard = function(text) {
window.document.body.removeChild(textarea);
}
};
+var client_players_Raw = function(main,player) {
+ this.playerEl = window.document.querySelector("#ytapiplayer");
+ this.main = main;
+ this.player = player;
+};
+client_players_Raw.__name__ = true;
+client_players_Raw.prototype = {
+ loadVideo: function(item) {
+ var _gthis = this;
+ this.video = window.document.createElement("video");
+ this.video.id = "videoplayer";
+ var url = this.main.tryLocalIp(item.url);
+ this.video.src = url;
+ this.video.controls = true;
+ var isTouch = 'ontouchstart' in window;
+ if(!isTouch) {
+ haxe_Timer.delay(function() {
+ _gthis.video.controls = false;
+ _gthis.video.onmouseover = function(e) {
+ _gthis.video.controls = true;
+ _gthis.video.onmouseover = null;
+ return _gthis.video.onmousemove = null;
+ };
+ return _gthis.video.onmousemove = _gthis.video.onmouseover;
+ },3000);
+ }
+ this.video.oncanplaythrough = ($_=this.player,$bind($_,$_.onCanBePlayed));
+ this.video.onseeking = ($_=this.player,$bind($_,$_.onSetTime));
+ this.video.onplay = ($_=this.player,$bind($_,$_.onPlay));
+ this.video.onpause = ($_=this.player,$bind($_,$_.onPause));
+ this.playerEl.appendChild(this.video);
+ this.video.pause();
+ }
+ ,removeVideo: function() {
+ if(this.video == null) {
+ return;
+ }
+ this.playerEl.removeChild(this.video);
+ this.video = null;
+ }
+ ,play: function() {
+ if(this.video == null) {
+ return;
+ }
+ this.video.play();
+ }
+ ,pause: function() {
+ if(this.video == null) {
+ return;
+ }
+ this.video.pause();
+ }
+ ,getTime: function() {
+ if(this.video == null) {
+ return 0;
+ }
+ return this.video.currentTime;
+ }
+ ,setTime: function(time) {
+ if(this.video == null) {
+ return;
+ }
+ this.video.currentTime = time;
+ }
+};
var haxe_Log = function() { };
haxe_Log.__name__ = true;
haxe_Log.formatOutput = function(v,infos) {
diff --git a/res/index.html b/res/index.html
index 7b51c56..bbc2751 100644
--- a/res/index.html
+++ b/res/index.html
@@ -99,7 +99,7 @@
<button class="btn btn-sm btn-default" id="mediarefresh" title="${refreshPlayer}"><span class="glyphicon glyphicon-retweet"></span></button>
<button class="btn btn-sm btn-default" id="fullscreenbtn" title="${fullscreenPlayer}"><span class="glyphicon glyphicon-fullscreen"></span></button>
<button class="btn btn-sm btn-default" id="getplaylist" title="${retrievePlaylistLinks}"><span class="glyphicon glyphicon-link"></span></button>
- <button class="btn btn-sm btn-default" id="voteskip" title="${voteForSkip}" disabled="disabled"><span class="glyphicon glyphicon-step-forward"></span></button>
+ <button class="btn btn-sm btn-default" id="voteskip" title="${voteForSkip}"><span class="glyphicon glyphicon-step-forward"></span></button>
</div>
</div>
</div>
diff --git a/src/Types.hx b/src/Types.hx
index 5706111..b98a6c2 100644
--- a/src/Types.hx
+++ b/src/Types.hx
@@ -35,7 +35,8 @@ typedef VideoItem = {
url:String,
title:String,
author:String,
- duration:Float
+ duration:Float,
+ isTemp:Bool
}
typedef WsEvent = {
@@ -47,6 +48,7 @@ typedef WsEvent = {
isUnknownClient:Bool,
clientName:String,
videoList:Array<VideoItem>,
+ itemPos:Int,
globalIp:String
},
?login:{
@@ -73,6 +75,9 @@ typedef WsEvent = {
?removeVideo:{
url:String
},
+ ?skipVideo:{
+ url:String
+ },
?pause:{
time:Float
},
@@ -108,6 +113,7 @@ enum abstract WsEventType(String) {
// var RemoveClient;
var AddVideo;
var RemoveVideo;
+ var SkipVideo;
var VideoLoaded;
var Pause;
var Play;
diff --git a/src/client/IPlayer.hx b/src/client/IPlayer.hx
new file mode 100644
index 0000000..9f224a8
--- /dev/null
+++ b/src/client/IPlayer.hx
@@ -0,0 +1,12 @@
+package client;
+
+import Types.VideoItem;
+
+interface IPlayer {
+ function loadVideo(item:VideoItem):Void;
+ function removeVideo():Void;
+ function play():Void;
+ function pause():Void;
+ function getTime():Float;
+ function setTime(time:Float):Void;
+}
diff --git a/src/client/Main.hx b/src/client/Main.hx
index b2e224d..19ca44b 100644
--- a/src/client/Main.hx
+++ b/src/client/Main.hx
@@ -90,6 +90,18 @@ class Main {
}
});
}
+ final voteSkip = ge("#voteskip");
+ voteSkip.onclick = e -> {
+ if (player.isListEmpty()) return;
+ final items = player.getItems();
+ final pos = player.getItemPos();
+ send({
+ type: SkipVideo,
+ skipVideo: {
+ url: items[pos].url
+ }
+ });
+ }
ge("#queue_next").onclick = e -> addVideoUrl(false);
ge("#queue_end").onclick = e -> addVideoUrl(true);
@@ -112,23 +124,31 @@ class Main {
function addVideoUrl(atEnd:Bool):Void {
final mediaUrl:InputElement = cast ge("#mediaurl");
+ final checkbox:InputElement = cast ge("#addfromurl").querySelector(".add-temp");
+ final isTemp = checkbox.checked;
final url = mediaUrl.value;
if (url.length == 0) return;
mediaUrl.value = "";
final url = ~/,(https?)/g.replace(url, "|$1");
final links = url.split("|");
- // if videos added as next, we need to load it in reverse order
- final link = (atEnd || player.isListEmpty()) ? links.shift() : links.pop();
- addVideo(link, atEnd, () -> addVideoArray(links, atEnd));
+ // if videos added as next, we need to load them in reverse order
+ if (!atEnd) {
+ // except first item when list empty
+ var first:Null<String> = null;
+ if (player.isListEmpty()) first = links.shift();
+ links.reverse();
+ if (player.isListEmpty()) links.unshift(first);
+ }
+ addVideoArray(links, atEnd, isTemp);
}
- function addVideoArray(links:Array<String>, atEnd:Bool):Void {
+ function addVideoArray(links:Array<String>, atEnd:Bool, isTemp:Bool):Void {
if (links.length == 0) return;
- final link = atEnd ? links.shift() : links.pop();
- addVideo(link, atEnd, () -> addVideoArray(links, atEnd));
+ final link = links.shift();
+ addVideo(link, atEnd, isTemp, () -> addVideoArray(links, atEnd, isTemp));
}
- function addVideo(url:String, atEnd:Bool, callback:()->Void):Void {
+ function addVideo(url:String, atEnd:Bool, isTemp:Bool, callback:()->Void):Void {
final protocol = Browser.location.protocol;
if (url.startsWith("/")) {
final host = Browser.location.hostname;
@@ -153,6 +173,7 @@ class Main {
title: name,
author: personal.name,
duration: duration,
+ isTemp: isTemp
},
atEnd: atEnd
}});
@@ -163,7 +184,7 @@ class Main {
public function toggleVideoElement():Bool {
if (player.hasVideo()) player.removeVideo();
else if (!player.isListEmpty()) {
- player.setVideo(player.getItems()[0]);
+ player.setVideo(player.getItemPos());
}
return player.hasVideo();
}
@@ -231,8 +252,8 @@ class Main {
addMessage(data.message.clientName, data.message.text);
case AddVideo:
- if (player.isListEmpty()) player.setVideo(data.addVideo.item);
player.addVideoItem(data.addVideo.item, data.addVideo.atEnd);
+ if (player.itemsLength() == 1) player.setVideo(0);
case VideoLoaded:
player.setTime(0);
@@ -242,6 +263,10 @@ class Main {
player.removeItem(data.removeVideo.url);
if (player.isListEmpty()) player.pause();
+ case SkipVideo:
+ player.skipItem(data.skipVideo.url);
+ if (player.isListEmpty()) player.pause();
+
case Pause:
if (isLeader()) return;
player.pause();
@@ -318,7 +343,7 @@ class Main {
for (message in connected.history) {
addMessage(message.name, message.text, message.time);
}
- player.setItems(connected.videoList);
+ player.setItems(connected.videoList, connected.itemPos);
}
function setConfig(config:Config):Void {
diff --git a/src/client/Player.hx b/src/client/Player.hx
index d8e4cb4..2c8f62e 100644
--- a/src/client/Player.hx
+++ b/src/client/Player.hx
@@ -1,10 +1,9 @@
package client;
-import haxe.Timer;
import js.html.Element;
-import js.html.VideoElement;
import js.Browser.document;
import client.Main.ge;
+import client.players.Raw;
import Types.VideoItem;
using StringTools;
using Lambda;
@@ -14,76 +13,93 @@ class Player {
final main:Main;
final items:Array<VideoItem> = [];
final videoItemsEl = ge("#queue");
- final player:Element = ge("#ytapiplayer");
+ final playerEl:Element = ge("#ytapiplayer");
+ var player:Null<IPlayer>;
+ var currentSrc = "";
+ var itemPos = 0;
var isLoaded = false;
var skipSetTime = false;
- var video:VideoElement;
+ final matchYoutube = ~/v=([A-z0-9_-]+)/;
public function new(main:Main):Void {
this.main = main;
}
- public function setVideo(item:VideoItem):Void {
+ function setPlayer(player:IPlayer):Void {
+ this.player = player;
+ }
+
+ function isYoutube(url:String):Bool {
+ if (!url.contains("youtube.com/")) return false;
+ if (!url.contains("youtu.be/")) return false;
+ if (!matchYoutube.match(url)) return false;
+ return true;
+ }
+
+ public function setVideo(i:Int):Void {
+ final item = items[i];
+ if (isYoutube(item.url)) {} // setPlayer(new Youtube(main, this));
+ else setPlayer(new Raw(main, this));
+
+ final childs = videoItemsEl.children;
+ if (childs[itemPos] != null) {
+ childs[itemPos].classList.remove("queue_active");
+ }
+ itemPos = i;
+ childs[itemPos].classList.add("queue_active");
+
+ currentSrc = item.url;
+ playerEl.textContent = "";
isLoaded = false;
- video = document.createVideoElement();
- video.id = "videoplayer";
- item.url = main.tryLocalIp(item.url);
- video.src = item.url;
- video.controls = true;
- final isTouch = untyped __js__("'ontouchstart' in window");
- if (!isTouch) Timer.delay(() -> {
- video.controls = false;
- video.onmouseover = e -> {
- video.controls = true;
- video.onmouseover = null;
- video.onmousemove = null;
+ player.loadVideo(item);
+ ge("#currenttitle").textContent = item.title;
+ }
+
+ public function removeVideo():Void {
+ currentSrc = "";
+ player.removeVideo();
+ ge("#currenttitle").textContent = Lang.get("nothingPlaying");
+ }
+
+ public function onCanBePlayed():Void {
+ if (!isLoaded) main.send({type: VideoLoaded});
+ isLoaded = true;
+ }
+
+ public function onPlay():Void {
+ if (!main.isLeader()) return;
+ main.send({
+ type: Play, play: {
+ time: getTime()
}
- video.onmousemove = video.onmouseover;
- }, 3000);
- video.oncanplaythrough = e -> {
- if (!isLoaded) main.send({type: VideoLoaded});
- isLoaded = true;
- }
- video.onseeking = e -> {
- if (skipSetTime) {
- skipSetTime = false;
- return;
+ });
+ }
+
+ public function onPause():Void {
+ if (!main.isLeader()) return;
+ main.send({
+ type: Pause, pause: {
+ time: getTime()
}
- if (!main.isLeader()) return;
- main.send({
- type: SetTime,
- setTime: {
- time: video.currentTime
- }
- });
- }
- video.onpause = e -> {
- if (!main.isLeader()) return;
- main.send({
- type: Pause,
- pause: {
- time: video.currentTime
- }
- });
- }
- video.onplay = e -> {
- if (!main.isLeader()) return;
- main.send({
- type: Play,
- play: {
- time: video.currentTime
- }
- });
+ });
+ }
+
+ public function onSetTime():Void {
+ if (skipSetTime) {
+ skipSetTime = false;
+ return;
}
- player.textContent = "";
- player.appendChild(video);
- ge("#currenttitle").textContent = item.title;
+ if (!main.isLeader()) return;
+ main.send({
+ type: SetTime, setTime: {
+ time: getTime()
+ }
+ });
}
public function addVideoItem(item:VideoItem, atEnd:Bool):Void {
- items.push(item);
final itemEl = nodeFromString(
- '<li class="queue_entry pluid-0 queue_temp queue_active" title="${Lang.get("addedBy")}: ${item.author}">
+ '<li class="queue_entry pluid-0" title="${Lang.get("addedBy")}: ${item.author}">
<a class="qe_title" href="${item.url}" target="_blank">${item.title.htmlEscape()}</a>
<span class="qe_time">${duration(item.duration)}</span>
<div class="qe_clear"></div>
@@ -103,6 +119,7 @@ class Player {
</div>
</li>'
);
+ if (item.isTemp) itemEl.classList.add("queue_temp");
final deleteBtn = itemEl.querySelector("#btn-delete");
deleteBtn.onclick = e -> {
main.send({
@@ -112,18 +129,13 @@ class Player {
}
});
}
+ if (atEnd) items.push(item);
+ else items.insert(itemPos + 1, item);
if (atEnd) videoItemsEl.appendChild(itemEl);
- else Utils.insertAtIndex(videoItemsEl, itemEl, 1);
+ else Utils.insertAtIndex(videoItemsEl, itemEl, itemPos + 1);
updateCounters();
}
- public function removeVideo():Void {
- if (video == null) return;
- player.removeChild(video);
- video = null;
- ge("#currenttitle").textContent = Lang.get("nothingPlaying");
- }
-
public function removeItem(url:String):Void {
for (child in videoItemsEl.children) {
if (child.querySelector(".qe_title").getAttribute("href") == url) {
@@ -132,15 +144,32 @@ class Player {
}
}
- items.remove(
- items.find(item -> item.url == url)
- );
+ final item = items.find(item -> item.url == url);
+ if (item == null) return;
+ var index = items.indexOf(item);
+ items.remove(item);
updateCounters();
- if (video == null) return;
- if (video.src == url) {
- if (items.length > 0) setVideo(items[0]);
+ if (index < itemPos) {
+ itemPos--;
+ return;
+ }
+ if (index != itemPos) return;
+ if (items.length == 0) return;
+ if (items[index] == null) index = 0;
+ setVideo(index);
+ }
+
+ public function skipItem(url:String):Void {
+ final item = items.find(item -> item.url == url);
+ if (item == null) return;
+ if (item.isTemp) {
+ removeItem(url);
+ return;
}
+ var index = items.indexOf(item) + 1;
+ if (index >= items.length) index = 0;
+ setVideo(index);
}
function updateCounters():Void {
@@ -152,15 +181,12 @@ class Player {
return items;
}
- public function setItems(list:Array<VideoItem>):Void {
+ public function setItems(list:Array<VideoItem>, ?pos:Int):Void {
clearItems();
+ if (pos != null) itemPos = pos;
if (list.length == 0) return;
- if (video == null || video.src != list[0].url) {
- setVideo(list[0]);
- }
- for (video in list) {
- addVideoItem(video, true);
- }
+ for (video in list) addVideoItem(video, true);
+ if (currentSrc != items[itemPos].url) setVideo(itemPos);
}
public function clearItems():Void {
@@ -172,7 +198,7 @@ class Player {
public function refresh():Void {
if (items.length == 0) return;
removeVideo();
- setVideo(items[0]);
+ setVideo(itemPos);
}
function duration(time:Float):String {
@@ -203,29 +229,37 @@ class Player {
return items.length == 0;
}
- public function hasVideo():Bool {
- return video != null;
+ public function itemsLength():Int {
+ return items.length;
}
- public function pause():Void {
- if (video == null) return;
- video.pause();
+ public function getItemPos():Int {
+ return itemPos;
+ }
+
+ public function hasVideo():Bool {
+ return player != null;
}
public function play():Void {
- if (video == null) return;
- video.play();
+ if (player == null) return;
+ player.play();
}
- public function setTime(time:Float, isLocal = true):Void {
- if (video == null) return;
- skipSetTime = isLocal;
- video.currentTime = time;
+ public function pause():Void {
+ if (player == null) return;
+ player.pause();
}
public function getTime():Float {
- if (video == null) return 0;
- return video.currentTime;
+ if (player == null) return 0;
+ return player.getTime();
+ }
+
+ public function setTime(time:Float, isLocal = true):Void {
+ if (player == null) return;
+ skipSetTime = isLocal;
+ player.setTime(time);
}
}
diff --git a/src/client/players/Raw.hx b/src/client/players/Raw.hx
new file mode 100644
index 0000000..41b421c
--- /dev/null
+++ b/src/client/players/Raw.hx
@@ -0,0 +1,72 @@
+package client.players;
+
+import haxe.Timer;
+import js.html.Element;
+import js.html.VideoElement;
+import js.Browser.document;
+import client.Main.ge;
+import Types.VideoItem;
+
+class Raw implements IPlayer {
+
+ final main:Main;
+ final player:Player;
+ var video:VideoElement;
+ final playerEl:Element = ge("#ytapiplayer");
+
+ public function new(main:Main, player:Player) {
+ this.main = main;
+ this.player = player;
+ }
+
+ public function loadVideo(item:VideoItem):Void {
+ video = document.createVideoElement();
+ video.id = "videoplayer";
+ final url = main.tryLocalIp(item.url);
+ video.src = url;
+ video.controls = true;
+ final isTouch = untyped __js__("'ontouchstart' in window");
+ if (!isTouch) Timer.delay(() -> {
+ video.controls = false;
+ video.onmouseover = e -> {
+ video.controls = true;
+ video.onmouseover = null;
+ video.onmousemove = null;
+ }
+ video.onmousemove = video.onmouseover;
+ }, 3000);
+ video.oncanplaythrough = player.onCanBePlayed;
+ video.onseeking = player.onSetTime;
+ video.onplay = player.onPlay;
+ video.onpause = player.onPause;
+ playerEl.appendChild(video);
+ video.pause();
+ }
+
+ public function removeVideo():Void {
+ if (video == null) return;
+ playerEl.removeChild(video);
+ video = null;
+ }
+
+ public function play():Void {
+ if (video == null) return;
+ video.play();
+ }
+
+ public function pause():Void {
+ if (video == null) return;
+ video.pause();
+ }
+
+ public function getTime():Float {
+ if (video == null) return 0;
+ return video.currentTime;
+ }
+
+ public function setTime(time:Float):Void {
+ if (video == null) return;
+ video.currentTime = time;
+ }
+
+}
diff --git a/src/server/Main.hx b/src/server/Main.hx
index 5d82ef1..e7895ca 100644
--- a/src/server/Main.hx
+++ b/src/server/Main.hx
@@ -31,6 +31,7 @@ class Main {
final videoList:Array<VideoItem> = [];
final videoTimer = new VideoTimer();
final messages:Array<Message> = [];
+ var itemPos = 0;
static function main():Void new Main();
@@ -99,6 +100,7 @@ class Main {
trace("Saving state...");
final data:ServerState = {
videoList: videoList,
+ itemPos: itemPos,
messages: messages,
timer: {
time: videoTimer.getTime(),
@@ -116,6 +118,7 @@ class Main {
videoList.resize(0);
messages.resize(0);
for (item in data.videoList) videoList.push(item);
+ itemPos = data.itemPos;
for (message in data.messages) messages.push(message);
videoTimer.start();
videoTimer.setTime(data.timer.time);
@@ -166,6 +169,7 @@ class Main {
for (client in clients) client.getData()
],
videoList: videoList,
+ itemPos: itemPos,
globalIp: globalIp
}
});
@@ -244,13 +248,17 @@ class Main {
case AddVideo:
final item = data.addVideo.item;
+ final local = '$localIp:$port';
+ if (item.url.contains(local)) {
+ item.url = item.url.replace(local, '$globalIp:$port');
+ }
item.author = client.name;
- final localOrigin = '$localIp:$port';
- if (item.url.indexOf(localOrigin) != -1) {
- item.url = item.url.replace(localOrigin, '$globalIp:$port');
+ if (videoList.exists(i -> i.url == item.url)) {
+ // TODO send server message
+ return;
}
if (data.addVideo.atEnd) videoList.push(item);
- else videoList.insert(1, item);
+ else videoList.insert(itemPos + 1, item);
broadcast(data);
// Initial timer start if VideoLoaded is not happen
if (videoList.length == 1) restartWaitTimer();
@@ -262,12 +270,30 @@ class Main {
case RemoveVideo:
if (videoList.length == 0) return;
final url = data.removeVideo.url;
- final isFirst = videoList[0].url == url;
- if (isFirst) videoTimer.stop();
- videoList.remove(
- videoList.find(item -> item.url == url)
- );
- if (isFirst && videoList.length > 0) restartWaitTimer();
+ final item = videoList.find(item -> item.url == url);
+ if (item == null) return;
+ final index = videoList.indexOf(item);
+ final isCurrent = videoList[itemPos].url == url;
+ if (index < itemPos) itemPos--;
+ videoList.remove(item);
+ if (isCurrent) {
+ if (itemPos >= videoList.length) itemPos = 0;
+ videoTimer.stop();
+ if (videoList.length > 0) restartWaitTimer();
+ }
+ broadcast(data);
+
+ case SkipVideo:
+ if (videoList.length == 0) return;
+ final item = videoList[itemPos];
+ if (item.url != data.skipVideo.url) return;
+
+ if (!item.isTemp) itemPos++;
+ else videoList.remove(item);
+ if (itemPos >= videoList.length) itemPos = 0;
+
+ videoTimer.stop();
+ if (videoList.length > 0) restartWaitTimer();
broadcast(data);
case Pause:
@@ -284,12 +310,12 @@ class Main {
case GetTime:
if (videoList.length == 0) return;
- if (videoTimer.getTime() > videoList[0].duration) {
+ if (videoTimer.getTime() > videoList[itemPos].duration) {
videoTimer.stop();
onMessage(client, {
- type: RemoveVideo,
- removeVideo: {
- url: videoList[0].url
+ type: SkipVideo,
+ skipVideo: {
+ url: videoList[itemPos].url
}
});
return;
@@ -338,14 +364,18 @@ class Main {
case ClearPlaylist:
videoTimer.stop();
videoList.resize(0);
+ itemPos = 0;
broadcast(data);
case ShufflePlaylist:
if (videoList.length == 0) return;
- final first = videoList.shift();
+ final current = videoList[itemPos];
+ videoList.remove(current);
Utils.shuffle(videoList);
- videoList.unshift(first);
- broadcast({type: UpdatePlaylist, updatePlaylist: {
+ videoList.insert(itemPos, current);
+ broadcast({
+ type: UpdatePlaylist,
+ updatePlaylist: {
videoList: videoList
}});
case UpdatePlaylist: // client-only
diff --git a/src/server/ServerState.hx b/src/server/ServerState.hx
index 06240e5..02cdc40 100644
--- a/src/server/ServerState.hx
+++ b/src/server/ServerState.hx
@@ -5,6 +5,7 @@ import Types.VideoItem;
typedef ServerState = {
videoList:Array<VideoItem>,
+ itemPos:Int,
messages:Array<Message>,
timer:{
time:Float,
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage