Added more function on dashboard
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import json
|
||||
import json, function as func
|
||||
|
||||
from typing import List
|
||||
|
||||
from discord import Member, VoiceChannel
|
||||
from discord.ext import commands
|
||||
from voicelink import Player, Track, Playlist, connect_channel, decode
|
||||
from voicelink import Player, Track, Playlist, NodePool, connect_channel, decode
|
||||
|
||||
class TempCtx():
|
||||
def __init__(self, author: Member, channel: VoiceChannel) -> None:
|
||||
@@ -16,11 +18,13 @@ def missingPermission(user_id:int):
|
||||
return payload
|
||||
|
||||
def error_msg(msg: str, *, user_id: int = None, guild_id: int = None, level: str = "info"):
|
||||
payload = {"op": "errorMsg", "level": level}
|
||||
payload = {"op": "errorMsg", "level": level, "msg": msg}
|
||||
if user_id:
|
||||
payload["user_id"]: user_id
|
||||
payload["user_id"] = user_id
|
||||
if guild_id:
|
||||
payload["guild_id"]: guild_id
|
||||
payload["guild_id"] = guild_id
|
||||
|
||||
return payload
|
||||
|
||||
@@ -54,7 +58,8 @@ async def initPlayer(player: Player, member: Member, data: dict):
|
||||
"current_position": 0 or player.position if player.is_playing else 0,
|
||||
"is_playing": player.is_playing,
|
||||
"is_paused": player.is_paused,
|
||||
"is_dj": player.is_privileged(member, check_user_join=False)
|
||||
"is_dj": player.is_privileged(member, check_user_join=False),
|
||||
"autoplay": player.settings.get("autoplay", False)
|
||||
}
|
||||
|
||||
async def skipTo(player: Player, member: Member, data: dict):
|
||||
@@ -243,21 +248,95 @@ async def updatePosition(player: Player, member: Member, data: dict):
|
||||
position = data.get("position");
|
||||
await player.seek(position, member);
|
||||
|
||||
async def toggleAutoplay(player: Player, member: Member, data: dict):
|
||||
if not player.is_privileged(member):
|
||||
return error_msg(player.get_msg('missingPerms_autoplay'))
|
||||
|
||||
check = data.get("status", False)
|
||||
player.settings['autoplay'] = check
|
||||
|
||||
if not player.is_playing:
|
||||
await player.do_next()
|
||||
|
||||
return {
|
||||
"op": "toggleAutoplay",
|
||||
"status": check,
|
||||
"requester_id": member.id
|
||||
}
|
||||
|
||||
async def closeConnection(player: Player, member: Member, data: dict):
|
||||
player._ipc_connection = False
|
||||
|
||||
async def getPlaylists(member: Member, data: dict):
|
||||
playlists: dict = await func.get_playlist(member.id, "playlist")
|
||||
if not playlists:
|
||||
return
|
||||
|
||||
for pId, pList in playlists.copy().items():
|
||||
if "type" in pList:
|
||||
if pList["type"] == "link":
|
||||
tracks: Playlist = await NodePool.get_node().get_tracks(pList["uri"], requester=member)
|
||||
if tracks:
|
||||
playlists[pId]["tracks"] = [ track.track_id for track in tracks.tracks ]
|
||||
|
||||
elif pList["type"] == "share":
|
||||
playlist = await func.get_playlist(pList["user"], "playlist", pList["referId"])
|
||||
if playlist:
|
||||
if member.id not in playlist["perms"]["read"]:
|
||||
await func.update_playlist(member.id, {f"playlist.{pId}": 1}, mode=False)
|
||||
del playlists[pId]
|
||||
continue
|
||||
playlists[pId]["tracks"] = playlist["tracks"]
|
||||
|
||||
return {
|
||||
"op": "getPlaylists",
|
||||
"playlists": playlists,
|
||||
"user_id": member.id
|
||||
}
|
||||
|
||||
async def addPlaylistTrack(member: Member, data: dict):
|
||||
track_id = data.get("track")
|
||||
pId = data.get("pId")
|
||||
if not track_id or not pId:
|
||||
return
|
||||
|
||||
playlist: dict = await func.get_playlist(member.id, 'playlist', pId)
|
||||
if not playlist:
|
||||
return
|
||||
|
||||
rank, max_p, max_t = await func.checkroles(member.id)
|
||||
if len(playlist["tracks"]) >= max_t:
|
||||
return error_msg(func.get_lang(member.guild.id, "playlistlimited").format(max_t), user_id=member.id)
|
||||
|
||||
if track_id in playlist['tracks']:
|
||||
return error_msg(func.get_lang(member.guild.id, "playlistrepeated"), user_id=member.id)
|
||||
|
||||
await func.update_playlist(member.id, {f'playlist.{pId}.tracks': track_id}, push=True)
|
||||
|
||||
async def removePlaylistTrack(member: Member, data: dict):
|
||||
track_id = data.get("track")
|
||||
pId = data.get("pId")
|
||||
if not track_id or not pId:
|
||||
return
|
||||
|
||||
await func.update_playlist(member.id, {f'playlist.{pId}.tracks': track_id }, pull=True, mode=False)
|
||||
|
||||
methods = {
|
||||
"initPlayer": initPlayer,
|
||||
"skipTo": skipTo,
|
||||
"backTo": backTo,
|
||||
"moveTrack": moveTrack,
|
||||
"addTracks": addTracks,
|
||||
"getTracks": getTracks,
|
||||
"shuffleTrack": shuffleTrack,
|
||||
"repeatTrack": repeatTrack,
|
||||
"removeTrack": removeTrack,
|
||||
"updatePause": updatePause,
|
||||
"updatePosition": updatePosition,
|
||||
"initPlayer": [initPlayer, False],
|
||||
"skipTo": [skipTo, False],
|
||||
"backTo": [backTo, False],
|
||||
"moveTrack": [moveTrack, False],
|
||||
"addTracks": [addTracks, True],
|
||||
"getTracks": [getTracks, True],
|
||||
"shuffleTrack": [shuffleTrack, False],
|
||||
"repeatTrack": [repeatTrack, False],
|
||||
"removeTrack": [removeTrack, False],
|
||||
"updatePause": [updatePause, False],
|
||||
"updatePosition": [updatePosition, False],
|
||||
"toggleAutoplay": [toggleAutoplay, False],
|
||||
"getPlaylists": [getPlaylists, False],
|
||||
"addPlaylistTrack": [addPlaylistTrack, False],
|
||||
"removePlaylistTrack": [removePlaylistTrack, False]
|
||||
}
|
||||
|
||||
async def process_methods(websocket, bot: commands.Bot, data: dict) -> None:
|
||||
@@ -268,6 +347,9 @@ async def process_methods(websocket, bot: commands.Bot, data: dict) -> None:
|
||||
guild, member = None, None
|
||||
guild_id = data.get("guild_id", None)
|
||||
user_id = data.get("user_id", None)
|
||||
if not user_id:
|
||||
return
|
||||
|
||||
if guild_id is None:
|
||||
user = bot.get_user(user_id)
|
||||
if not user:
|
||||
@@ -278,24 +360,32 @@ async def process_methods(websocket, bot: commands.Bot, data: dict) -> None:
|
||||
if m.voice and m.voice.channel:
|
||||
guild = g
|
||||
member = m
|
||||
|
||||
break
|
||||
else:
|
||||
guild = bot.get_guild(guild_id)
|
||||
member = guild.get_member(user_id)
|
||||
|
||||
if not guild or not member:
|
||||
if not member:
|
||||
return
|
||||
|
||||
player: Player = guild.voice_client
|
||||
if not player:
|
||||
if method.__name__ != "getTracks":
|
||||
return
|
||||
player: Player = await connect_channel(member, bot)
|
||||
|
||||
try:
|
||||
resp: dict = await method(player, member, data)
|
||||
if 'player' in method[0].__code__.co_varnames:
|
||||
if not guild:
|
||||
return
|
||||
|
||||
player: Player = guild.voice_client
|
||||
if not player:
|
||||
if not method[1]:
|
||||
return
|
||||
player: Player = await connect_channel(member, bot)
|
||||
|
||||
resp: dict = await method[0](player, member, data)
|
||||
else:
|
||||
resp: dict = await method[0](member, data)
|
||||
|
||||
if resp:
|
||||
await websocket.send(json.dumps(resp))
|
||||
|
||||
except Exception as e:
|
||||
payload = {
|
||||
"op": "errorMsg",
|
||||
|
||||
@@ -119,6 +119,8 @@ a {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
background-color: transparent;
|
||||
transition: background-color 0.5s ease;
|
||||
}
|
||||
|
||||
.header .left {
|
||||
@@ -126,7 +128,7 @@ a {
|
||||
}
|
||||
|
||||
.header .left p {
|
||||
font-size: 20;
|
||||
font-size: 20px;
|
||||
padding: 12px;
|
||||
border-radius: 10px;
|
||||
margin: 0rem 0.4rem;
|
||||
@@ -245,7 +247,7 @@ a {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.main .toastContrainer {
|
||||
.toastContrainer {
|
||||
position: absolute;
|
||||
left: 2%;
|
||||
z-index: 2;
|
||||
@@ -334,11 +336,26 @@ a {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.player-controller:hover .seek-bar {
|
||||
height: 9px;
|
||||
}
|
||||
|
||||
.progress-bar-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.position-info {
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
left: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.progress-bar-container:hover .position-info {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.seek-bar {
|
||||
position: absolute;
|
||||
appearance: none;
|
||||
@@ -349,10 +366,6 @@ a {
|
||||
background-color: rgb(73, 73, 73);
|
||||
}
|
||||
|
||||
.seek-bar:hover {
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.seek-bar::-webkit-slider-thumb {
|
||||
appearance: none;
|
||||
width: 1px;
|
||||
@@ -383,11 +396,11 @@ a {
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
margin-left: .8rem;
|
||||
font-size: 20;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.player-controller .left .control #play-pause-button {
|
||||
font-size: 30;
|
||||
.player-controller .left .control #play-pause-btn {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.player-controller .left .position {
|
||||
@@ -412,13 +425,30 @@ a {
|
||||
}
|
||||
|
||||
.player-controller .center .data {
|
||||
margin-left: 1rem;
|
||||
overflow: hidden;
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
||||
.player-controller .center .data p {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.player-controller .center .desc {
|
||||
color: grey;
|
||||
font-size: 13;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.player-controller .center i {
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
font-size: 20px;
|
||||
color: gray;
|
||||
transition: color .2s ease-in-out;
|
||||
}
|
||||
|
||||
.player-controller .center i:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.player-controller .right {
|
||||
@@ -433,7 +463,7 @@ a {
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
margin-left: .8rem;
|
||||
font-size: 20;
|
||||
font-size: 20px;
|
||||
color: gray;
|
||||
transition: color .2s ease-in-out;
|
||||
}
|
||||
@@ -442,18 +472,41 @@ a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.main .list {
|
||||
flex: 1;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.main .auto-play {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.main .queue-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.main .queue-list ul {
|
||||
list-style: none;
|
||||
width: 95%;
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.track {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
max-width: 100%;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
border-radius: .5rem;
|
||||
@@ -507,6 +560,11 @@ a {
|
||||
background-color: rgba(157, 157, 157, 50%);
|
||||
}
|
||||
|
||||
.track i:active {
|
||||
transform: scale(0.85);
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.track .left img {
|
||||
padding: 5px;
|
||||
height: 50;
|
||||
@@ -525,15 +583,7 @@ a {
|
||||
|
||||
.track .left .info p.desc {
|
||||
color: grey;
|
||||
font-size: 13;
|
||||
}
|
||||
|
||||
.main .queue-list ul {
|
||||
list-style: none;
|
||||
width: 90%;
|
||||
padding: 1rem;
|
||||
padding-bottom: 5rem;
|
||||
max-height: 300px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.users-bar {
|
||||
@@ -591,25 +641,25 @@ a {
|
||||
.context-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.context-menu ul {
|
||||
list-style: none;
|
||||
padding: .5rem;
|
||||
border-radius: .5rem;
|
||||
min-width: 150px;
|
||||
padding: .5rem 0;
|
||||
border-radius: .3rem;
|
||||
background-color: rgb(37, 37, 37);
|
||||
}
|
||||
|
||||
.context-menu li {
|
||||
padding: .5rem;
|
||||
border-radius: .3rem;
|
||||
padding: .5rem 1rem;
|
||||
cursor: pointer;
|
||||
transition: background-color .2s;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.context-menu li:hover {
|
||||
padding: .5rem;
|
||||
background-color: rgb(98, 98, 98);
|
||||
}
|
||||
|
||||
@@ -617,6 +667,190 @@ a {
|
||||
margin-right: .8rem;
|
||||
}
|
||||
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 52px;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: var(--grey-hover);
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
input:checked+.slider {
|
||||
background-color: #2196F3;
|
||||
}
|
||||
|
||||
input:focus+.slider {
|
||||
box-shadow: 0 0 1px #2196F3;
|
||||
}
|
||||
|
||||
input:checked+.slider:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
/* Rounded sliders */
|
||||
.slider.round {
|
||||
border-radius: 34px;
|
||||
}
|
||||
|
||||
.slider.round:before {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.playlists {
|
||||
flex-grow: 1;
|
||||
padding: 0rem 4rem;
|
||||
}
|
||||
|
||||
.playlists-grid {
|
||||
display: grid;
|
||||
grid-gap: 1.5rem;
|
||||
padding: 2rem;
|
||||
grid-template-rows: repeat(auto-fit, 250px);
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 200px));
|
||||
grid-auto-rows: 250px;
|
||||
}
|
||||
|
||||
.images {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
border-radius: .5rem;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
transition: all 0.2s ease-in-out;
|
||||
opacity: 0;
|
||||
animation: created 1s ease-in-out forwards;
|
||||
}
|
||||
|
||||
.images img {
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.images i.action {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: none;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
margin: .5rem;
|
||||
padding: .5rem .9rem;
|
||||
background-color: transparent;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.images i.play {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
margin: 1rem;
|
||||
padding: .7rem .84rem;
|
||||
display: none;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.images::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-shadow: inset 0px 10px 40px rgba(0, 0, 0, 0.5);
|
||||
opacity: 0;
|
||||
transition: all 0.2s ease-in-out;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.images:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.images:hover i {
|
||||
display: block;
|
||||
text-align: center;
|
||||
border-radius: 3rem;
|
||||
}
|
||||
|
||||
.images i.action:hover {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.images i.action:active {
|
||||
transform: scale(0.85);
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.images i.play:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.images i.play:active {
|
||||
transform: scale(0.85);
|
||||
box-shadow: 0 0 10px rgba(230, 230, 230, 0.3);
|
||||
}
|
||||
|
||||
.playlist .info {
|
||||
margin-top: .5rem;
|
||||
}
|
||||
.playlist .info p {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.playlist .info p.desc {
|
||||
font-size: 14px;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
@keyframes created {
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 935px) {
|
||||
.main {
|
||||
flex-direction: column;
|
||||
@@ -624,7 +858,7 @@ a {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.main .toastContrainer {
|
||||
.toastContrainer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -643,6 +877,19 @@ a {
|
||||
ul {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.playlists-grid {
|
||||
grid-gap: 1rem;
|
||||
padding: 1rem;
|
||||
grid-template-rows: repeat(auto-fit, 200px);
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 150px));
|
||||
grid-auto-rows: 200px;
|
||||
}
|
||||
|
||||
.images {
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
|
||||
@@ -3,9 +3,11 @@ $(document).ready(function () {
|
||||
const largeImage = document.getElementById("largeImage");
|
||||
const img = document.getElementById("image");
|
||||
const canvas = document.createElement('canvas');
|
||||
const $positionInfo = $('.position-info');
|
||||
|
||||
var startPos = null;
|
||||
var selectedTrack = null;
|
||||
var selectedPlaylistId = null;
|
||||
|
||||
var typingTimer;
|
||||
var doneTypingInterval = 2000;
|
||||
@@ -31,13 +33,13 @@ $(document).ready(function () {
|
||||
})
|
||||
.catch(error => { return });
|
||||
|
||||
} catch (e) {}
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
img.onload = function () {
|
||||
try {
|
||||
$("#image").fadeIn(200);
|
||||
} catch (e) {}
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
function getMainColorsFromImage(numColors) {
|
||||
@@ -72,24 +74,64 @@ $(document).ready(function () {
|
||||
});
|
||||
}
|
||||
|
||||
$("#seek-bar").on('mousemove', function (e) {
|
||||
var position = e.pageX - $(this).offset().left;
|
||||
var duration = $(this).width();
|
||||
var percentage = (position / duration) * 100;
|
||||
|
||||
var time = player.msToReadableTime(percentage * player.currentTrack?.length / 100);
|
||||
$positionInfo.text(time).css({
|
||||
left: e.pageX - $positionInfo.outerWidth() / 2,
|
||||
});
|
||||
});
|
||||
|
||||
$('body').click(function (event) {
|
||||
var $target = $(event.target);
|
||||
if (!$target.closest(".search-container").length && !$target.is("#search-input") && $('#search-result-list').css("display") != 'none') {
|
||||
$("#search-result-list").fadeOut(200);
|
||||
}
|
||||
|
||||
if (!$target.closest(".users-bar").is('.users-bar') &&
|
||||
!$target.closest("#users-button").is('#users-button') &&
|
||||
else if (!$target.closest(".users-bar").is('.users-bar') &&
|
||||
!$target.closest("#users-btn").is('#users-btn') &&
|
||||
$(".users-bar").hasClass("active")) {
|
||||
$(".users-bar").removeClass("active");
|
||||
$("#users-button").css({ "color": "" });
|
||||
$("#users-btn").css({ "color": "" });
|
||||
}
|
||||
|
||||
if (!$target.closest(".action").is(".action") &&
|
||||
else if (!$target.closest(".action").is(".action") &&
|
||||
!$target.closest("#context-menu li").is("#context-menu li") &&
|
||||
$("#context-menu").css("display") != "none") {
|
||||
$("#context-menu").fadeOut(200);
|
||||
}
|
||||
|
||||
else if ($target.closest(".images").is(".images")) {
|
||||
selectedPlaylistId = $target.closest(".playlist").data("value");
|
||||
|
||||
if ($target.closest(".action").is(".action")) {
|
||||
const $contextMenu = $("#playlist-context-menu")
|
||||
|
||||
const menuHeight = $contextMenu.outerHeight();
|
||||
const windowHeight = $(window).height();
|
||||
const topPosition = event.pageY + 30;
|
||||
if (topPosition + menuHeight > windowHeight) {
|
||||
// If the menu would go out of the page, position it above the btn instead
|
||||
$contextMenu.css({ "left": `${event.pageX - 130}px`, "top": `${event.pageY - menuHeight - 30}px` }).fadeIn(200);
|
||||
} else {
|
||||
$contextMenu.css({ "left": `${event.pageX - 130}px`, "top": `${topPosition}px` }).fadeIn(200);
|
||||
}
|
||||
} else if ($target.closest(".play").is(".play")) {
|
||||
if (selectedPlaylistId in player.playlists) {
|
||||
if ("tracks" in player.playlists[selectedPlaylistId]) {
|
||||
player.send({ "op": "addTracks", "tracks": player.playlists[selectedPlaylistId]["tracks"] });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$target.closest(".action").is(".action") &&
|
||||
$("#playlist-context-menu").css("display") != "none") {
|
||||
$("#playlist-context-menu").fadeOut(200);
|
||||
}
|
||||
});
|
||||
|
||||
$(function () {
|
||||
@@ -117,8 +159,16 @@ $(document).ready(function () {
|
||||
|
||||
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
|
||||
const menuHeight = $("#context-menu").outerHeight();
|
||||
const windowHeight = $(window).height();
|
||||
const topPosition = event.pageY + 30;
|
||||
if (topPosition + menuHeight > windowHeight) {
|
||||
// If the menu would go out of the page, position it above the btn instead
|
||||
$("#context-menu").css({ "left": `${event.pageX - 130}px`, "top": `${event.pageY - menuHeight - 30}px` }).fadeIn(200);
|
||||
} else {
|
||||
$("#context-menu").css({ "left": `${event.pageX - 130}px`, "top": `${topPosition}px` }).fadeIn(200);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < position) {
|
||||
@@ -134,7 +184,7 @@ $(document).ready(function () {
|
||||
var index = $(this).index();
|
||||
var track = player.searchList[index];
|
||||
if (track != undefined) {
|
||||
player.send({ "op": "addTracks", "tracks": [track] })
|
||||
player.send({ "op": "addTracks", "tracks": [track] });
|
||||
}
|
||||
$("#search-result-list").fadeOut(200);
|
||||
})
|
||||
@@ -159,15 +209,15 @@ $(document).ready(function () {
|
||||
}
|
||||
})
|
||||
|
||||
$('#play-pause-button').on('click', function () {
|
||||
$('#play-pause-btn').on('click', function () {
|
||||
player.togglePause();
|
||||
});
|
||||
|
||||
$('#skip-button').on('click', function () {
|
||||
$('#skip-btn').on('click', function () {
|
||||
player.skipTo();
|
||||
});
|
||||
|
||||
$('#back-button').on('click', function () {
|
||||
$('#back-btn').on('click', function () {
|
||||
player.backTo();
|
||||
});
|
||||
|
||||
@@ -175,15 +225,15 @@ $(document).ready(function () {
|
||||
player.seekTo($(this).val());
|
||||
})
|
||||
|
||||
$("#repeat-button").on('click', function () {
|
||||
$("#repeat-btn").on('click', function () {
|
||||
player.repeatMode();
|
||||
})
|
||||
|
||||
$("#shuffle-button").on('click', function () {
|
||||
$("#shuffle-btn").on('click', function () {
|
||||
player.shuffle();
|
||||
})
|
||||
|
||||
$("#users-button").on('click', function () {
|
||||
$("#users-btn").on('click', function () {
|
||||
const userBar = $(".users-bar")
|
||||
userBar.toggleClass("active");
|
||||
if (userBar.hasClass("active")) {
|
||||
@@ -194,14 +244,51 @@ $(document).ready(function () {
|
||||
|
||||
})
|
||||
|
||||
$("#remove-track-button").on('click', function () {
|
||||
$("#auto-play").on("click", function () {
|
||||
var checkbox = $(this).is(':checked');
|
||||
player.send({ "op": "toggleAutoplay", "status": checkbox })
|
||||
});
|
||||
|
||||
$("#remove-track-btn").on('click', function () {
|
||||
player.removeTrack(selectedTrack?.position, selectedTrack?.track)
|
||||
$("#context-menu").fadeOut(200);
|
||||
})
|
||||
|
||||
$("#copy-track-button").on('click', function () {
|
||||
$("#copy-track-btn").on('click', function () {
|
||||
navigator.clipboard.writeText(selectedTrack?.track.uri);
|
||||
$("#context-menu").fadeOut(200);
|
||||
})
|
||||
|
||||
$("#homeBtn").on('click', function () {
|
||||
$("#playlists").fadeOut(200, function () {
|
||||
$("#main").fadeIn(200);
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
$("#playlistBtn").on('click', function () {
|
||||
$("#main").fadeOut(200, function () {
|
||||
if (player.playlists == null) {
|
||||
player.send({ "op": "getPlaylists" });
|
||||
}
|
||||
$("#playlists").fadeIn(200);
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
$("#like-btn").on('click', function () {
|
||||
var currentTrack = player.currentTrack;
|
||||
if (currentTrack != undefined) {
|
||||
if (currentTrack.isStream) {
|
||||
return player.showToast("error", "You are not allowed to add streaming videos to your playlist!");
|
||||
}
|
||||
if ($(this).hasClass("fa-regular")) {
|
||||
$(this).removeClass("fa-regular").addClass("fa-solid");
|
||||
player.send({"op": "addPlaylistTrack", "track": currentTrack.track_id, "pId": "200"})
|
||||
} else {
|
||||
$(this).removeClass("fa-solid").addClass("fa-regular");
|
||||
player.send({"op": "removePlaylistTrack", "track": currentTrack.track_id, "pId": "200"})
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
@@ -98,7 +98,7 @@ class Track {
|
||||
} else {
|
||||
this.imageUrl = object.thumbnail;
|
||||
}
|
||||
|
||||
this.isStream = object.isStream;
|
||||
this.length = Number(object.length);
|
||||
this.track_id = object.track_id;
|
||||
this.uri = object.uri;
|
||||
@@ -106,19 +106,6 @@ class Track {
|
||||
}
|
||||
|
||||
const decoders = [
|
||||
undefined,
|
||||
undefined,
|
||||
(input, track_id) => {
|
||||
const title = input.readUTF();
|
||||
const author = input.readUTF();
|
||||
const length = input.readLong();
|
||||
const identifier = input.readUTF();
|
||||
const isStream = input.readBoolean();
|
||||
const uri = input.readBoolean() ? input.readUTF() : null;
|
||||
const source = input.readUTF();
|
||||
|
||||
return {track_id, title, author, length, identifier, isStream, uri, thumbnail: null, source, position: 0n };
|
||||
},
|
||||
(input, track_id) => {
|
||||
const title = input.readUTF();
|
||||
const author = input.readUTF();
|
||||
@@ -129,7 +116,19 @@ const decoders = [
|
||||
const thumbnail = input.readBoolean() ? input.readUTF() : null;
|
||||
const source = input.readUTF();
|
||||
|
||||
return {track_id, title, author, length, identifier, isStream, uri, thumbnail, source, position: 0n };
|
||||
return { track_id, title, author, length, identifier, isStream, uri, thumbnail, source, position: 0n };
|
||||
},
|
||||
undefined,
|
||||
(input, track_id) => {
|
||||
const title = input.readUTF();
|
||||
const author = input.readUTF();
|
||||
const length = input.readLong();
|
||||
const identifier = input.readUTF();
|
||||
const isStream = input.readBoolean();
|
||||
const uri = input.readBoolean() ? input.readUTF() : null;
|
||||
const source = input.readUTF();
|
||||
|
||||
return { track_id, title, author, length, identifier, isStream, uri, thumbnail: null, source, position: 0n };
|
||||
}
|
||||
]
|
||||
function decode(track_id) {
|
||||
@@ -137,7 +136,7 @@ function decode(track_id) {
|
||||
const flags = input.readInt();
|
||||
const version = input.readByte();
|
||||
|
||||
const decoder = decoders[version];
|
||||
const decoder = decoders[version];
|
||||
return new Track(decoder(input, track_id));
|
||||
}
|
||||
|
||||
@@ -151,6 +150,7 @@ const actions = {
|
||||
player.current_position = data['current_position'];
|
||||
player.repeat = data['repeat_mode'];
|
||||
player.channelName = data["channel_name"];
|
||||
player.autoplay = data["autoplay"];
|
||||
data["users"].forEach(user => {
|
||||
player.addUser(user);
|
||||
})
|
||||
@@ -324,11 +324,49 @@ const actions = {
|
||||
player.showToast(data["requester_id"], msg);
|
||||
},
|
||||
|
||||
errorMsg: function (player, data) {
|
||||
var level = data["level"];
|
||||
player.showToast(level, data["msg"]);
|
||||
}
|
||||
toggleAutoplay: function (player, data) {
|
||||
var status = data["status"];
|
||||
player.autoplay = status;
|
||||
},
|
||||
|
||||
errorMsg: function (player, data) {
|
||||
console.log("hello")
|
||||
player.showToast(data["level"], data["msg"]);
|
||||
},
|
||||
|
||||
getPlaylists: function (player, data) {
|
||||
const playlists = data["playlists"]
|
||||
player.playlists = playlists;
|
||||
|
||||
$("#playlists-grid").empty();
|
||||
for (let key in playlists) {
|
||||
const pList = playlists[key];
|
||||
let pDiv = $(`<div class="playlist" data-value="${key}">`);
|
||||
let iDiv = $("<div class='images'>");
|
||||
let infoDiv = $("<div class='info'>");
|
||||
|
||||
if (pList["tracks"] === 0) {
|
||||
continue;
|
||||
} else {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (pList["tracks"][i] == undefined) {
|
||||
continue;
|
||||
}
|
||||
var track = decode(pList["tracks"][i])
|
||||
let img = $("<img>").attr("src", track.imageUrl);
|
||||
iDiv.append(img);
|
||||
|
||||
}
|
||||
}
|
||||
iDiv.append(`<i class="fa-solid fa-ellipsis-vertical action"></i>`);
|
||||
iDiv.append(`<i class="fa-solid fa-play play"></i>`);
|
||||
pDiv.append(iDiv);
|
||||
infoDiv.append(`<p>${pList["name"]}</p>`);
|
||||
infoDiv.append(`<p class="desc">${pList["type"]} • ${pList["tracks"].length} Tracks</p>`);
|
||||
pDiv.append(infoDiv);
|
||||
$("#playlists-grid").append(pDiv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Player {
|
||||
@@ -351,8 +389,10 @@ class Player {
|
||||
this.volume = 100;
|
||||
this.last_update = 0;
|
||||
this.is_connected = true;
|
||||
this.autoplay = false;
|
||||
|
||||
this.channelName = "";
|
||||
this.playlists = null;
|
||||
}
|
||||
|
||||
handleMessage(data) {
|
||||
@@ -553,8 +593,7 @@ class Player {
|
||||
updateInfo() {
|
||||
var currentTrack = this.currentTrack;
|
||||
if (currentTrack == undefined) {
|
||||
$("#title").text("");
|
||||
$("#author").text("");
|
||||
$(".control-container .center").fadeOut();
|
||||
$("#position").text("00:00");
|
||||
$("#length").text("00:00");
|
||||
$("#image").fadeOut(100, function () { $(this).removeAttr("src"); });
|
||||
@@ -564,7 +603,9 @@ class Player {
|
||||
} else {
|
||||
$("#title").text(currentTrack.title);
|
||||
$("#author").text(currentTrack.author);
|
||||
$(".control-container .center").fadeIn();
|
||||
$("#length").text(this.msToReadableTime(currentTrack.length));
|
||||
$("#auto-play").prop('checked', this.autoplay);
|
||||
|
||||
var image = "";
|
||||
if (currentTrack.source == "youtube") {
|
||||
@@ -576,17 +617,25 @@ class Player {
|
||||
$(".thumbnail-background").fadeOut(100);
|
||||
$("#largeImage").fadeOut(function () {
|
||||
$(this).attr("src", image);
|
||||
|
||||
|
||||
});
|
||||
$("#image").fadeOut(function() {
|
||||
$("#image").fadeOut(function () {
|
||||
$(this).attr("src", currentTrack.imageUrl);
|
||||
});
|
||||
|
||||
if (this.playlists != null) {
|
||||
if (this.playlists["200"]["tracks"].includes(currentTrack.track_id)) {
|
||||
$("#like-btn").removeClass("fa-regular").addClass("fa-solid");
|
||||
} else {
|
||||
$("#like-btn").removeClass("fa-solid").addClass("fa-regular");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$("#channel-name").text((this.channelName == "") ? "Not Found" : this.channelName);
|
||||
var play_pause_btn = $("#play-pause-button");
|
||||
var repeat_btn = $("#repeat-button");
|
||||
var play_pause_btn = $("#play-pause-btn");
|
||||
var repeat_btn = $("#repeat-btn");
|
||||
if (this.is_paused || currentTrack == undefined) {
|
||||
play_pause_btn.removeClass('fa-pause').addClass('fa-play');
|
||||
if (this.timer.getIsRunning()) {
|
||||
|
||||
@@ -55,16 +55,33 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="main" id="main">
|
||||
<div class="toastContrainer"></div>
|
||||
<div class="thumbnail">
|
||||
<img id="largeImage" alt="" />
|
||||
<div class="thumbnail-background"></div>
|
||||
</div>
|
||||
<div class="queue-list">
|
||||
<ul id="sortable">
|
||||
</ul>
|
||||
<div class="list">
|
||||
<div class="auto-play">
|
||||
<div>
|
||||
<p>Autoplay</p>
|
||||
<p>Similar track will be to the queue</p>
|
||||
</div>
|
||||
<label class="switch">
|
||||
<input type="checkbox" id="auto-play">
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="queue-list">
|
||||
<ul id="sortable">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="playlists" id="playlists" style="display: None">
|
||||
<div class="playlists-grid" id="playlists-grid">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -80,13 +97,14 @@
|
||||
<div class="player-controller">
|
||||
<div class="progress-bar-container">
|
||||
<input type="range" value="0" min="0" max="500" class="seek-bar" id="seek-bar">
|
||||
<div class="position-info"></div>
|
||||
</div>
|
||||
<div class="control-container">
|
||||
<div class="left">
|
||||
<div class="control">
|
||||
<i id="back-button" class="fa-solid fa-backward-step"></i>
|
||||
<i id="play-pause-button" class="fa-solid fa-play"></i>
|
||||
<i id="skip-button" class="fa-solid fa-forward-step"></i>
|
||||
<i id="back-btn" class="fa-solid fa-backward-step"></i>
|
||||
<i id="play-pause-btn" class="fa-solid fa-play"></i>
|
||||
<i id="skip-btn" class="fa-solid fa-forward-step"></i>
|
||||
</div>
|
||||
|
||||
<div class="position">
|
||||
@@ -101,12 +119,13 @@
|
||||
<p id="title"></p>
|
||||
<p class="desc" id="author"></p>
|
||||
</div>
|
||||
<i class="fa-regular fa-thumbs-up" id="like-btn"></i>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<i id="repeat-button" class="fa-solid fa-repeat"></i>
|
||||
<i id="shuffle-button" class="fa-solid fa-shuffle"></i>
|
||||
<i id="users-button" class="fa-solid fa-user-group"></i>
|
||||
<i id="repeat-btn" class="fa-solid fa-repeat"></i>
|
||||
<i id="shuffle-btn" class="fa-solid fa-shuffle"></i>
|
||||
<i id="users-btn" class="fa-solid fa-user-group"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -114,8 +133,14 @@
|
||||
|
||||
<div id="context-menu" class="context-menu">
|
||||
<ul>
|
||||
<li id="remove-track-button"><i class="fa-solid fa-trash"></i>Remove</li>
|
||||
<li id="copy-track-button"><i class="fa-solid fa-share"></i>Copy Song Link</li>
|
||||
<li id="remove-track-btn"><i class="fa-solid fa-trash"></i>Remove</li>
|
||||
<li id="copy-track-btn"><i class="fa-solid fa-share"></i>Copy Song Link</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="playlist-context-menu" class="context-menu">
|
||||
<ul>
|
||||
<li id="remove-playlist-btn"><i class="fa-solid fa-trash"></i>Remove</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -183,7 +183,7 @@ def callback():
|
||||
response = requests.post(f'{DISCORD_API_BASE_URL}/oauth2/token', data=data)
|
||||
token_data = json.loads(response.content.decode('utf-8'))
|
||||
session.permanent = True
|
||||
app.permanent_session_lifetime = timedelta(days=20)
|
||||
app.permanent_session_lifetime = timedelta(days=30)
|
||||
session['discord_token'] = token_data.get("access_token")
|
||||
|
||||
return redirect(url_for("home"))
|
||||
|
||||
Reference in New Issue
Block a user