diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Types.hx | 12 | ||||
| -rw-r--r-- | src/VideoList.hx | 67 | ||||
| -rw-r--r-- | src/client/Main.hx | 9 | ||||
| -rw-r--r-- | src/client/Player.hx | 125 | ||||
| -rw-r--r-- | src/client/Utils.hx | 9 | ||||
| -rw-r--r-- | src/server/Main.hx | 44 |
6 files changed, 208 insertions, 58 deletions
diff --git a/src/Types.hx b/src/Types.hx index b98a6c2..17b010b 100644 --- a/src/Types.hx +++ b/src/Types.hx @@ -97,6 +97,15 @@ typedef WsEvent = { ?setLeader:{ clientName:String }, + ?playItem:{ + pos:Int + }, + ?setNextItem:{ + pos:Int + }, + ?toggleItemType:{ + pos:Int + }, ?updatePlaylist:{ videoList:Array<VideoItem> } @@ -121,6 +130,9 @@ enum abstract WsEventType(String) { var SetTime; var Rewind; var SetLeader; + var PlayItem; + var SetNextItem; + var ToggleItemType; var ClearChat; var ClearPlaylist; var ShufflePlaylist; diff --git a/src/VideoList.hx b/src/VideoList.hx new file mode 100644 index 0000000..2f13b6c --- /dev/null +++ b/src/VideoList.hx @@ -0,0 +1,67 @@ +package; + +import Types.VideoItem; + +// TODO move itemPos to abstract +// typedef VideoListData = { +// items:Array<VideoItem>, +// itemPos:Int +// } + +@:forward +abstract VideoList(Array<VideoItem>) from Array<VideoItem> to Array<VideoItem> { + + public function new() { + this = []; + } + + @:arrayAccess + public inline function get(i:Int):VideoItem { + return this[i]; + } + + @:arrayAccess + public inline function set(k:Int, v:VideoItem):VideoItem { + return this[k] = v; + } + + public function findIndex(f:(item:VideoItem) -> Bool):Int { + var i = 0; + for (v in this) { + if (f(v)) return i; + i++; + } + return -1; + } + + public function addItem(item:VideoItem, atEnd:Bool, itemPos:Int):Void { + if (atEnd) this.push(item); + else this.insert(itemPos + 1, item); + } + + public function setNextItem(pos:Int, itemPos:Int):Void { + final next = this[pos]; + this.remove(next); + this.insert(itemPos + 1, next); + } + + public function toggleItemType(pos:Int):Void { + this[pos].isTemp = !this[pos].isTemp; + } + + public function removeItem(index:Int, itemPos:Int):Int { + if (index < itemPos) itemPos--; + this.remove(this[index]); + if (itemPos >= this.length) itemPos = 0; + return itemPos; + } + + public function skipItem(itemPos:Int):Int { + final item = this[itemPos]; + if (!item.isTemp) itemPos++; + else this.remove(item); + if (itemPos >= this.length) itemPos = 0; + return itemPos; + } + +} diff --git a/src/client/Main.hx b/src/client/Main.hx index 19ca44b..1804ca1 100644 --- a/src/client/Main.hx +++ b/src/client/Main.hx @@ -307,6 +307,15 @@ class Main { setLeaderButton(isLeader()); if (isLeader()) player.setTime(player.getTime(), false); + case PlayItem: + player.setVideo(data.playItem.pos); + + case SetNextItem: + player.setNextItem(data.setNextItem.pos); + + case ToggleItemType: + player.toggleItemType(data.toggleItemType.pos); + case ClearChat: clearChat(); diff --git a/src/client/Player.hx b/src/client/Player.hx index 2c8f62e..3941bdb 100644 --- a/src/client/Player.hx +++ b/src/client/Player.hx @@ -11,7 +11,7 @@ using Lambda; class Player { final main:Main; - final items:Array<VideoItem> = []; + final items = new VideoList(); final videoItemsEl = ge("#queue"); final playerEl:Element = ge("#ytapiplayer"); var player:Null<IPlayer>; @@ -23,6 +23,58 @@ class Player { public function new(main:Main):Void { this.main = main; + initItemButtons(); + } + + function initItemButtons():Void { + final queue = ge("#queue"); + queue.onclick = e -> { + final btn:Element = cast e.target; + final item = btn.parentElement.parentElement; + final i = Utils.getIndex(item.parentElement, item); + if (btn.classList.contains("qbtn-play")) { + main.send({ + type: PlayItem, playItem: { + pos: i + } + }); + } + if (btn.classList.contains("qbtn-next")) { + main.send({ + type: SetNextItem, setNextItem: { + pos: i + } + }); + } + if (btn.classList.contains("qbtn-tmp")) { + main.send({ + type: ToggleItemType, toggleItemType: { + pos: i + } + }); + } + if (btn.classList.contains("qbtn-delete")) { + main.send({ + type: RemoveVideo, removeVideo: { + url: item.querySelector(".qe_title").getAttribute("href") + } + }); + } + } + } + + public function setNextItem(pos:Int):Void { + items.setNextItem(pos, itemPos); + + final next = videoItemsEl.children[pos]; + videoItemsEl.removeChild(next); + Utils.insertAtIndex(videoItemsEl, next, itemPos + 1); + } + + public function toggleItemType(pos:Int):Void { + items.toggleItemType(pos); + final el = videoItemsEl.children[pos]; + setItemElementType(el, items[pos].isTemp); } function setPlayer(player:IPlayer):Void { @@ -108,67 +160,60 @@ class Player { <span class="glyphicon glyphicon-play"></span>${Lang.get("play")} </button> <button class="btn btn-xs btn-default qbtn-next"> - <span class="glyphicon glyphicon-share-alt"></span>${Lang.get("skip")} + <span class="glyphicon glyphicon-share-alt"></span>${Lang.get("setNext")} </button> <button class="btn btn-xs btn-default qbtn-tmp"> - <span class="glyphicon glyphicon-flag"></span>${Lang.get("makePermanent")} + <span class="glyphicon glyphicon-flag"></span> </button> - <button class="btn btn-xs btn-default qbtn-delete" id="btn-delete"> + <button class="btn btn-xs btn-default qbtn-delete"> <span class="glyphicon glyphicon-trash"></span>${Lang.get("delete")} </button> </div> </li>' ); - if (item.isTemp) itemEl.classList.add("queue_temp"); - final deleteBtn = itemEl.querySelector("#btn-delete"); - deleteBtn.onclick = e -> { - main.send({ - type: RemoveVideo, - removeVideo: { - url: itemEl.querySelector(".qe_title").getAttribute("href") - } - }); - } - if (atEnd) items.push(item); - else items.insert(itemPos + 1, item); + items.addItem(item, atEnd, itemPos); + setItemElementType(itemEl, item.isTemp); if (atEnd) videoItemsEl.appendChild(itemEl); else Utils.insertAtIndex(videoItemsEl, itemEl, itemPos + 1); updateCounters(); } + function setItemElementType(item:Element, isTemp:Bool):Void { + final text = isTemp ? Lang.get("makePermanent") : Lang.get("makeTemporary"); + item.querySelector(".qbtn-tmp").innerHTML = '<span class="glyphicon glyphicon-flag"></span>$text'; + if (isTemp) item.classList.add("queue_temp"); + else item.classList.remove("queue_temp"); + } + public function removeItem(url:String):Void { + removeElementItem(url); + var index = items.findIndex(item -> item.url == url); + if (index == -1) return; + + final isCurrent = items[itemPos].url == url; + itemPos = items.removeItem(index, itemPos); + updateCounters(); + + if (isCurrent && items.length > 0) { + setVideo(itemPos); + } + } + + function removeElementItem(url:String):Void { for (child in videoItemsEl.children) { if (child.querySelector(".qe_title").getAttribute("href") == url) { videoItemsEl.removeChild(child); break; } } - - final item = items.find(item -> item.url == url); - if (item == null) return; - var index = items.indexOf(item); - items.remove(item); - updateCounters(); - - 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; + var index = items.findIndex(item -> item.url == url); + if (index == -1) return; + if (items[index].isTemp) removeElementItem(url); + index = items.skipItem(index); + if (items.length == 0) return; setVideo(index); } @@ -177,7 +222,7 @@ class Player { ge("#pllength").textContent = totalDuration(); } - public function getItems():Array<VideoItem> { + public function getItems():VideoList { return items; } diff --git a/src/client/Utils.hx b/src/client/Utils.hx index 37de672..69671d2 100644 --- a/src/client/Utils.hx +++ b/src/client/Utils.hx @@ -16,6 +16,15 @@ class Utils { else parent.insertBefore(child, parent.children[i]); } + public static function getIndex(parent:Element, child:Element):Int { + var i = 0; + for (el in parent.children) { + if (el == child) break; + i++; + } + return i; + } + public static function toggleFullScreen(el:Element):Bool { var state = true; final doc:Dynamic = document; diff --git a/src/server/Main.hx b/src/server/Main.hx index e7895ca..e5b50c8 100644 --- a/src/server/Main.hx +++ b/src/server/Main.hx @@ -28,7 +28,7 @@ class Main { final config:Config; final clients:Array<Client> = []; final freeIds:Array<Int> = []; - final videoList:Array<VideoItem> = []; + final videoList = new VideoList(); final videoTimer = new VideoTimer(); final messages:Array<Message> = []; var itemPos = 0; @@ -257,8 +257,7 @@ class Main { // TODO send server message return; } - if (data.addVideo.atEnd) videoList.push(item); - else videoList.insert(itemPos + 1, item); + videoList.addItem(item, data.addVideo.atEnd, itemPos); broadcast(data); // Initial timer start if VideoLoaded is not happen if (videoList.length == 1) restartWaitTimer(); @@ -270,16 +269,13 @@ class Main { case RemoveVideo: if (videoList.length == 0) return; final url = data.removeVideo.url; - final item = videoList.find(item -> item.url == url); - if (item == null) return; - final index = videoList.indexOf(item); + var index = videoList.findIndex(item -> item.url == url); + if (index == -1) return; + 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(); + itemPos = videoList.removeItem(index, itemPos); + if (isCurrent && videoList.length > 0) { + restartWaitTimer(); } broadcast(data); @@ -287,12 +283,7 @@ class Main { 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(); + itemPos = videoList.skipItem(itemPos); if (videoList.length > 0) restartWaitTimer(); broadcast(data); @@ -357,6 +348,22 @@ class Main { }); } + case PlayItem: + itemPos = data.playItem.pos; + restartWaitTimer(); + broadcast(data); + + case SetNextItem: + final pos = data.setNextItem.pos; + if (pos == itemPos || pos == itemPos + 1) return; + videoList.setNextItem(pos, itemPos); + broadcast(data); + + case ToggleItemType: + final pos = data.toggleItemType.pos; + videoList.toggleItemType(pos); + broadcast(data); + case ClearChat: messages.resize(0); if (client.isAdmin) broadcast(data); @@ -426,6 +433,7 @@ class Main { var loadedClientsCount = 0; function restartWaitTimer():Void { + videoTimer.stop(); if (waitVideoStart != null) waitVideoStart.stop(); waitVideoStart = Timer.delay(startVideoPlayback, 3000); } |
