From 70c744ddb39b513fb81b37a623071068162ea3dd Mon Sep 17 00:00:00 2001 From: Choco <94597336+ChocoMeow@users.noreply.github.com> Date: Sat, 25 Mar 2023 16:45:49 +0800 Subject: [PATCH] Add action to track in dashboard --- cogs/basic.py | 13 ++++++++-- voicelink/queue.py | 26 ++++++++++++-------- web/ipc/methods.py | 24 +++++++++++++++++++ web/static/css/main.css | 51 +++++++++++++++++++++++++++++++++++++++- web/static/js/action.js | 23 +++++++++++++++++- web/static/js/objects.js | 47 ++++++++++++++++++++++++++++++++++-- web/templates/index.html | 7 ++++++ 7 files changed, 175 insertions(+), 16 deletions(-) diff --git a/cogs/basic.py b/cogs/basic.py index b946366..558ac98 100644 --- a/cogs/basic.py +++ b/cogs/basic.py @@ -507,8 +507,17 @@ class Basic(commands.Cog): if not player.is_privileged(ctx.author): return await ctx.send(player.get_msg('missingPerms_queue'), ephemeral=True) - removedTrack = player.queue.remove(position1, position2, member) - await ctx.send(player.get_msg('removed').format(removedTrack)) + removedTrack = player.queue.remove(position1, position2, member=member) + + if self.bot.ipc.connections and removedTrack: + await player.send_ws({ + "op": "removeTrack", + "positions": [track["position"] for track in removedTrack], + "track_ids": [track["track"].track_id for track in removedTrack], + "current_queue_position": player.queue._position + }, requester=ctx.author) + + await ctx.send(player.get_msg('removed').format(len(removedTrack))) @commands.hybrid_command(name="forward", aliases=get_aliases("forward")) @app_commands.describe(position="Input a amount that you to forward to. Exmaple: 1:20") diff --git a/voicelink/queue.py b/voicelink/queue.py index 7b8800d..ba13881 100644 --- a/voicelink/queue.py +++ b/voicelink/queue.py @@ -138,19 +138,25 @@ class Queue: def remove(self, index:int, index2:int, member: Member = None): pos = self._position - 1 - if not index2: + + if index2 is None: index2 = index - count = 0 + + if index2 < index: + temp = index + index = index2 + index2 = temp + try: - for track in self._queue[pos + index: pos + index2 + 1]: + count = [] + for i, track in enumerate(self._queue[pos + index: pos + index2 + 1], start=0): if member: - if track.requester == member: - self._queue.remove(track) - count += 1 - continue - else: - self._queue.remove(track) - count += 1 + if track.requester != member: + continue + + self._queue.remove(track) + count.append({"position": pos + index + i, "track": track}) + return count except: raise OutofList(self.get_msg("voicelinkOutofList")) diff --git a/web/ipc/methods.py b/web/ipc/methods.py index 58eb74f..89db079 100644 --- a/web/ipc/methods.py +++ b/web/ipc/methods.py @@ -159,6 +159,29 @@ async def repeatTrack(player, member: Member, data: dict): await player.set_repeat() +async def removeTrack(player, member: Member, data: dict): + if not player.is_privileged(member): + return missingPermission(member.id) + + position = data.get("position") + verify_id = data.get("track_id") + + track = player.queue._queue[position] + if track.track_id == verify_id: + player.queue._queue.remove(track) + + if position < player.queue._position: + player.queue._position -= 1 + + return { + "op": "removeTrack", + "positions": [position], + "track_ids": [track.track_id], + "current_queue_position": player.queue._position, + "requester_id": member.id, + "guild_id": player.guild.id + } + async def updatePause(player, member: Member, data: dict): if not player.is_privileged(member): return missingPermission(member.id) @@ -207,6 +230,7 @@ methods = { "getTracks": getTracks, "shuffleTrack": shuffleTrack, "repeatTrack": repeatTrack, + "removeTrack": removeTrack, "updatePause": updatePause, "updatePosition": updatePosition, } diff --git a/web/static/css/main.css b/web/static/css/main.css index 20ea6c4..b1bf3d1 100644 --- a/web/static/css/main.css +++ b/web/static/css/main.css @@ -444,6 +444,14 @@ a { background-color: rgba(142, 142, 142, 20%); } +.track:hover .time { + display: none; +} + +.track:hover i.action { + display: block; +} + .track.active { background-color: rgba(142, 142, 142, 30%); } @@ -457,12 +465,25 @@ a { overflow: hidden; } -.track .left i { +.track i { cursor: move; margin-right: 1rem; font-size: 20px; } +.track i.action { + display: none; + padding: .5rem 1rem; + border-radius: 50%; + margin-right: 0; + cursor: pointer; + transition: background-color .2s; +} + +.track i.action:hover { + background-color: rgba(157, 157, 157, 50%); +} + .track .left img { padding: 5px; height: 50; @@ -543,6 +564,34 @@ a { margin-right: .5rem; } +.context-menu { + display: none; + position: absolute; +} + +.context-menu ul { + list-style: none; + padding: .5rem; + border-radius: .5rem; + background-color: rgb(37, 37, 37); +} + +.context-menu li { + padding: .5rem; + border-radius: .3rem; + cursor: pointer; + transition: background-color .2s; +} + +.context-menu li:hover { + padding: .5rem; + background-color: rgb(98, 98, 98); +} + +.context-menu i { + margin-right: .8rem; +} + @media (max-width: 935px) { .main { flex-direction: column; diff --git a/web/static/js/action.js b/web/static/js/action.js index 0edca72..70c6372 100644 --- a/web/static/js/action.js +++ b/web/static/js/action.js @@ -1,6 +1,7 @@ $(document).ready(function () { const player = new Player(userId); var startPos = null; + var selectedTrack = null; var typingTimer; var doneTypingInterval = 2000; @@ -19,6 +20,11 @@ $(document).ready(function () { $("#users-button").css({ "color": "" }); } + if (!$target.closest(".action").is(".action") && + !$target.closest("#context-menu li").is("#context-menu li") && + $("#context-menu").css("display") != "none") { + $("#context-menu").fadeOut(200); + } }); $(function () { @@ -40,10 +46,16 @@ $(document).ready(function () { }); }); - $('#sortable').on('click', 'li', function () { + $('#sortable').on('click', 'li', function (event) { var index = $(this).index(); var position = player.current_queue_position; + if ($(event.target).hasClass('action')) { + selectedTrack = {position: index, track: player.queue[index]}; + $("#context-menu").css({ "left": `${event.pageX - 150}px`, "top": `${event.pageY + 30}px` }).fadeIn(200); + return + } + if (index < position) { player.backTo(position - index); } else if (index > position) { @@ -117,4 +129,13 @@ $(document).ready(function () { }) + $("#remove-track-button").on('click', function() { + player.removeTrack(selectedTrack?.position, selectedTrack?.track) + $("#context-menu").fadeOut(200); + }) + + $("#copy-track-button").on('click', function() { + navigator.clipboard.writeText(selectedTrack?.track.uri); + $("#context-menu").fadeOut(200); + }) }); diff --git a/web/static/js/objects.js b/web/static/js/objects.js index 21541a7..8028d1d 100644 --- a/web/static/js/objects.js +++ b/web/static/js/objects.js @@ -172,6 +172,37 @@ const actions = { player.repeat = data["repeatMode"]; }, + removeTrack: function (player, data) { + const positions = data["positions"]; + const trackIds = data["track_ids"]; + var removeTracks = [] + var newQueue = []; + + player.queue.forEach((track, index) => { + if (positions.includes(index)) { + if (trackIds.includes(track.track_id)) { + console.log("removed" + index); + removeTracks.push(track); + } else { + return player.send({ "op": "initPlayer" }) + } + } else { + newQueue.push(track); + } + }) + + player.queue = newQueue; + player.initSortable(); + + if (removeTracks.length == 1) { + var msg = `Removed ${removeTracks[0].title} from the queue.`; + } else { + var msg = `Removed ${removeTracks.length} tracks from the queue.`; + } + + player.showToast(data["requester_id"], msg); + }, + errorMsg: function (player, data) { var level = data["level"]; player.showToast(level, data["msg"]); @@ -239,7 +270,7 @@ class Player { $('#sortable').empty(); for (var i in this.queue) { var track = this.queue[i]; - $("#sortable").append(`
${track.title}
${track.author}
${this.msToReadableTime(track.length)}
${track.title}
${track.author}
${this.msToReadableTime(track.length)}
${track.title}
${track.author}
${this.msToReadableTime(track.length)}
${track.title}
${track.author}
${this.msToReadableTime(track.length)}