aboutsummaryrefslogtreecommitdiffstats
path: root/src/client
diff options
context:
space:
mode:
authorRblSb <msrblsb@gmail.com>2020-02-24 01:19:30 +0300
committerRblSb <msrblsb@gmail.com>2020-02-24 01:19:30 +0300
commitc561fb9e2e42e4968f2b48cd535f208e90f8c12c (patch)
tree0c7e1ffc99744aabbb240864b371b9555a611d92 /src/client
parent113b06e895f5dc752e8393c2a4f3f1669a7d0aab (diff)
More playlist and video control
Diffstat (limited to 'src/client')
-rw-r--r--src/client/Buttons.hx42
-rw-r--r--src/client/Main.hx80
-rw-r--r--src/client/MobileView.hx23
-rw-r--r--src/client/Player.hx59
-rw-r--r--src/client/Utils.hx63
5 files changed, 197 insertions, 70 deletions
diff --git a/src/client/Buttons.hx b/src/client/Buttons.hx
index a5fbfa4..f72fbb3 100644
--- a/src/client/Buttons.hx
+++ b/src/client/Buttons.hx
@@ -1,8 +1,8 @@
package client;
+import haxe.Timer;
import js.html.KeyboardEvent;
import js.html.InputElement;
-import js.html.ButtonElement;
import js.html.Element;
import client.Main.ge;
import js.Browser.window;
@@ -77,16 +77,48 @@ class Buttons {
extendPlayer.onclick = e -> {
if (extendPlayer.classList.contains("active")) {
split.setSizes([40, 60]);
- ge('#userlist').style.width = "90px";
+ ge("#userlist").style.width = "90px";
} else {
split.setSizes([20, 80]);
- ge('#userlist').style.width = "80px";
+ ge("#userlist").style.width = "80px";
}
extendPlayer.classList.toggle("active");
- window.dispatchEvent(new Event('resize'));
+ window.dispatchEvent(new Event("resize"));
}
- final showMediaUrl:ButtonElement = cast ge("#showmediaurl");
+ final mediaRefresh = ge("#mediarefresh");
+ mediaRefresh.onclick = e -> {
+ main.refreshPlayer();
+ }
+ final fullscreenBtn = ge("#fullscreenbtn");
+ fullscreenBtn.onclick = e -> {
+ final el = ge("#ytapiplayer");
+ Utils.toggleFullScreen(el);
+ }
+ final getPlaylist = ge("#getplaylist");
+ getPlaylist.onclick = e -> {
+ final text = main.getPlaylistLinks().join(",");
+ Utils.copyToClipboard(text);
+ final icon = getPlaylist.firstElementChild;
+ icon.classList.remove("glyphicon-link");
+ icon.classList.add("glyphicon-ok");
+ Timer.delay(() -> {
+ icon.classList.add("glyphicon-link");
+ icon.classList.remove("glyphicon-ok");
+ }, 2000);
+ }
+ final clearPlaylist = ge("#clearplaylist");
+ clearPlaylist.onclick = e -> {
+ if (!window.confirm(Lang.get("clearPlaylistConfirm"))) return;
+ main.send({type: ClearPlaylist});
+ }
+ final shufflePlaylist = ge("#shuffleplaylist");
+ shufflePlaylist.onclick = e -> {
+ if (!window.confirm(Lang.get("shufflePlaylistConfirm"))) return;
+ main.send({type: ShufflePlaylist});
+ }
+
+ final showMediaUrl = ge("#showmediaurl");
showMediaUrl.onclick = e -> {
ge("#showmediaurl").classList.toggle("collapsed");
ge("#showmediaurl").classList.toggle("active");
diff --git a/src/client/Main.hx b/src/client/Main.hx
index dc5a803..96353f8 100644
--- a/src/client/Main.hx
+++ b/src/client/Main.hx
@@ -71,8 +71,7 @@ class Main {
Buttons.init(this);
MobileView.init();
- final leaderBtn = ge("#leader_btn");
- leaderBtn.onclick = (e) -> {
+ ge("#leader_btn").onclick = e -> {
// change button style before answer
setLeaderButton(!personal.isLeader);
final name = personal.isLeader ? "" : personal.name;
@@ -84,11 +83,10 @@ class Main {
});
}
- // TODO next/end
- ge("#queue_next").onclick = (e:MouseEvent) -> addVideoUrl();
- ge("#queue_end").onclick = (e:MouseEvent) -> addVideoUrl();
+ ge("#queue_next").onclick = (e:MouseEvent) -> addVideoUrl(false);
+ ge("#queue_end").onclick = (e:MouseEvent) -> addVideoUrl(true);
ge("#mediaurl").onkeydown = function(e:KeyboardEvent) {
- if (e.keyCode == 13) addVideoUrl();
+ if (e.keyCode == 13) addVideoUrl(true);
}
}
@@ -100,26 +98,55 @@ class Main {
return personal.isAdmin;
}
- function addVideoUrl():Void {
+ function addVideoUrl(atEnd:Bool):Void {
final mediaUrl:InputElement = cast ge("#mediaurl");
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));
+ }
+
+ function addVideoArray(links:Array<String>, atEnd:Bool):Void {
+ if (links.length == 0) return;
+ final link = atEnd ? links.shift() : links.pop();
+ addVideo(link, atEnd, () -> addVideoArray(links, atEnd));
+ }
+
+ function addVideo(url:String, atEnd:Bool, callback:()->Void):Void {
+ if (!url.startsWith("http")) url = '${Browser.location.protocol}//$url';
var name = url.substr(url.lastIndexOf('/') + 1);
final matchName = ~/^(.+)\./;
if (matchName.match(name)) name = matchName.matched(1);
else name = Lang.get("rawVideo");
- getRemoteVideoDuration(mediaUrl.value, (duration:Float) -> {
+ getRemoteVideoDuration(url, (duration:Float) -> {
send({
type: AddVideo, addVideo: {
item: {
url: url,
title: name,
author: personal.name,
- duration: duration
- }
- }});
+ duration: duration,
+ },
+ atEnd: atEnd
+ }});
+ callback();
});
- mediaUrl.value = "";
+ }
+
+ public function refreshPlayer():Void {
+ player.refresh();
+ }
+
+ public function getPlaylistLinks():Array<String> {
+ final items = player.getItems();
+ return [
+ for (item in items) item.url
+ ];
}
function getRemoteVideoDuration(src:String, callback:(duration:Float)->Void):Void {
@@ -131,15 +158,10 @@ class Main {
callback(0);
}
video.onloadedmetadata = () -> {
- player.removeChild(video);
+ if (player.contains(video)) player.removeChild(video);
callback(video.duration);
}
- prepend(player, video);
- }
-
- function prepend(parent:Element, child:Element):Void {
- if (parent.firstChild == null) parent.appendChild(child);
- else parent.insertBefore(child, parent.firstChild);
+ Utils.prepend(player, video);
}
function onMessage(e):Void {
@@ -150,6 +172,7 @@ class Main {
switch (data.type) {
case Connected:
onConnected(data);
+ onTimeGet.run();
case Login:
onLogin(data.login.clients, data.login.clientName);
case LoginError:
@@ -167,7 +190,7 @@ class Main {
addMessage(data.message.clientName, data.message.text);
case AddVideo:
if (player.isListEmpty()) player.setVideo(data.addVideo.item);
- player.addVideoItem(data.addVideo.item);
+ player.addVideoItem(data.addVideo.item, data.addVideo.atEnd);
case VideoLoaded:
player.setTime(0);
player.play();
@@ -192,10 +215,10 @@ class Main {
player.setTime(time, false);
return;
}
- if (Math.abs(time - newTime) < 2) return;
- player.setTime(newTime);
if (!data.getTime.paused) player.play();
else player.pause();
+ if (Math.abs(time - newTime) < 2) return;
+ player.setTime(newTime);
case SetTime:
final newTime = data.setTime.time;
final time = player.getTime();
@@ -210,6 +233,12 @@ class Main {
if (isLeader()) player.setTime(player.getTime(), false);
case ClearChat:
ge("#messagebuffer").innerHTML = "";
+ case ClearPlaylist:
+ player.clearItems();
+ if (player.isListEmpty()) player.pause();
+ case ShufflePlaylist: // server-only
+ case UpdatePlaylist:
+ player.setItems(data.updatePlaylist.videoList);
}
}
@@ -233,12 +262,7 @@ class Main {
for (message in connected.history) {
addMessage(message.name, message.text, message.time);
}
- final list = connected.videoList;
- if (list.length == 0) return;
- player.setVideo(list[0]);
- for (video in connected.videoList) {
- player.addVideoItem(video);
- }
+ player.setItems(connected.videoList);
}
function setConfig(config:Config):Void {
diff --git a/src/client/MobileView.hx b/src/client/MobileView.hx
index 976958b..ad788df 100644
--- a/src/client/MobileView.hx
+++ b/src/client/MobileView.hx
@@ -8,7 +8,7 @@ class MobileView {
public static function init():Void {
final mvbtn = ge("#mv_btn");
mvbtn.onclick = e -> {
- final mobileView = toggleFullScreen();
+ final mobileView = Utils.toggleFullScreen(document.documentElement);
if (mobileView) {
document.body.classList.add('mobile-view');
mvbtn.classList.add('active');
@@ -27,25 +27,4 @@ class MobileView {
}
}
- static function toggleFullScreen():Bool {
- var state = true;
- final doc:Dynamic = document;
- if (document.fullscreenElement == null &&
- doc.mozFullScreenElement == null &&
- doc.webkitFullscreenElement == null) {
- if (document.documentElement.requestFullscreen != null) {
- document.documentElement.requestFullscreen();
- } else if (doc.documentElement.mozRequestFullScreen != null) {
- doc.documentElement.mozRequestFullScreen();
- } else if (doc.documentElement.webkitRequestFullscreen != null) {
- doc.documentElement.webkitRequestFullscreen(untyped Element.ALLOW_KEYBOARD_INPUT);
- } else state = false;
- } else {
- if (doc.cancelFullScreen != null) doc.cancelFullScreen();
- else if (doc.mozCancelFullScreen != null) doc.mozCancelFullScreen();
- else if (doc.webkitCancelFullScreen != null) doc.webkitCancelFullScreen();
- state = false;
- }
- return state;
- }
}
diff --git a/src/client/Player.hx b/src/client/Player.hx
index 19e0c84..34ef716 100644
--- a/src/client/Player.hx
+++ b/src/client/Player.hx
@@ -1,7 +1,5 @@
package client;
-import js.html.LIElement;
-import js.html.UListElement;
import js.html.Element;
import js.html.VideoElement;
import js.Browser.document;
@@ -29,7 +27,7 @@ class Player {
video.id = "videoplayer";
video.src = item.url;
video.controls = true;
- video.oncanplaythrough = (e) -> {
+ video.oncanplaythrough = e -> {
if (!isLoaded) main.send({type: VideoLoaded});
isLoaded = true;
}
@@ -46,7 +44,7 @@ class Player {
}
});
}
- video.onpause = (e) -> {
+ video.onpause = e -> {
if (!main.isLeader()) return;
main.send({
type: Pause,
@@ -55,7 +53,7 @@ class Player {
}
});
}
- video.onplay = (e) -> {
+ video.onplay = e -> {
if (!main.isLeader()) return;
main.send({
type: Play,
@@ -69,9 +67,9 @@ class Player {
ge("#currenttitle").innerHTML = item.title;
}
- public function addVideoItem(item:VideoItem):Void {
+ public function addVideoItem(item:VideoItem, atEnd:Bool):Void {
items.push(item);
- final itemEl:LIElement = cast nodeFromString(
+ final itemEl = nodeFromString(
'<li class="queue_entry pluid-0 queue_temp queue_active" title="${Lang.get("addedBy")}: ${item.author}">
<a class="qe_title" href="${item.url}" target="_blank">${item.title}</a>
<span class="qe_time">${duration(item.duration)}</span>
@@ -93,7 +91,7 @@ class Player {
</li>'
);
final deleteBtn = itemEl.querySelector("#btn-delete");
- deleteBtn.onclick = (e) -> {
+ deleteBtn.onclick = e -> {
main.send({
type: RemoveVideo,
removeVideo: {
@@ -101,22 +99,22 @@ class Player {
}
});
}
- videoItemsEl.appendChild(itemEl);
- ge("#plcount").innerHTML = '${items.length} ${Lang.get("videos")}';
- ge("#pllength").innerHTML = totalDuration();
+ if (atEnd) videoItemsEl.appendChild(itemEl);
+ else Utils.insertAtIndex(videoItemsEl, itemEl, 1);
+ updateCounters();
}
public function removeVideo():Void {
+ if (video == null) return;
player.removeChild(video);
video = null;
ge("#currenttitle").innerHTML = Lang.get("nothingPlaying");
}
public function removeItem(url:String):Void {
- final list = ge("#queue");
- for (child in list.children) {
+ for (child in videoItemsEl.children) {
if (child.querySelector(".qe_title").getAttribute("href") == url) {
- list.removeChild(child);
+ videoItemsEl.removeChild(child);
break;
}
}
@@ -128,13 +126,44 @@ class Player {
if (video.src == url) {
if (items.length > 0) setVideo(items[0]);
}
+ updateCounters();
+ }
+
+ function updateCounters():Void {
ge("#plcount").innerHTML = '${items.length} ${Lang.get("videos")}';
ge("#pllength").innerHTML = totalDuration();
}
+ public function getItems():Array<VideoItem> {
+ return items;
+ }
+
+ public function setItems(list:Array<VideoItem>):Void {
+ clearItems();
+ if (list.length == 0) return;
+ if (video == null || video.src != list[0].url) {
+ setVideo(list[0]);
+ }
+ for (video in list) {
+ addVideoItem(video, true);
+ }
+ }
+
+ public function clearItems():Void {
+ items.resize(0);
+ videoItemsEl.innerHTML = "";
+ updateCounters();
+ }
+
+ public function refresh():Void {
+ if (items.length == 0) return;
+ removeVideo();
+ setVideo(items[0]);
+ }
+
function duration(time:Float):String {
final h = Std.int(time / 60 / 60);
- final m = Std.int(time / 60);
+ final m = Std.int(time / 60) - h * 60;
final s = Std.int(time % 60);
var time = '$m:';
if (m < 10) time = '0$time';
diff --git a/src/client/Utils.hx b/src/client/Utils.hx
new file mode 100644
index 0000000..37de672
--- /dev/null
+++ b/src/client/Utils.hx
@@ -0,0 +1,63 @@
+package client;
+
+import js.html.Element;
+import js.Browser.document;
+import js.Browser.window;
+
+class Utils {
+
+ public static function prepend(parent:Element, child:Element):Void {
+ if (parent.firstChild == null) parent.appendChild(child);
+ else parent.insertBefore(child, parent.firstChild);
+ }
+
+ public static function insertAtIndex(parent:Element, child:Element, i:Int) {
+ if (i >= parent.children.length) parent.appendChild(child);
+ else parent.insertBefore(child, parent.children[i]);
+ }
+
+ public static function toggleFullScreen(el:Element):Bool {
+ var state = true;
+ final doc:Dynamic = document;
+ final el2:Dynamic = el;
+ if (document.fullscreenElement == null &&
+ doc.mozFullScreenElement == null &&
+ doc.webkitFullscreenElement == null) {
+ if (el.requestFullscreen != null) {
+ el.requestFullscreen();
+ } else if (el2.mozRequestFullScreen != null) {
+ el2.mozRequestFullScreen();
+ } else if (el2.webkitRequestFullscreen != null) {
+ el2.webkitRequestFullscreen(untyped Element.ALLOW_KEYBOARD_INPUT);
+ } else state = false;
+ } else {
+ if (doc.cancelFullScreen != null) doc.cancelFullScreen();
+ else if (doc.mozCancelFullScreen != null) doc.mozCancelFullScreen();
+ else if (doc.webkitCancelFullScreen != null) doc.webkitCancelFullScreen();
+ state = false;
+ }
+ return state;
+ }
+
+ public static function copyToClipboard(text:String):Void {
+ final clipboardData = (window : Dynamic).clipboardData;
+ if (clipboardData != null && clipboardData.setData != null) {
+ // IE-specific code path to prevent textarea being shown while dialog is visible.
+ clipboardData.setData("Text", text);
+ return;
+ } else if ((document : Dynamic).queryCommandSupported != null) {
+ final textarea = document.createTextAreaElement();
+ textarea.textContent = text;
+ // Prevent scrolling to bottom of page in Microsoft Edge.
+ textarea.style.position = "fixed";
+ document.body.appendChild(textarea);
+ textarea.select();
+ try {
+ // Security exception may be thrown by some browsers.
+ document.execCommand("copy");
+ }
+ document.body.removeChild(textarea);
+ }
+ }
+
+}
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage