Added more function on dashboard

This commit is contained in:
Choco
2023-05-27 21:03:27 +08:00
parent a44b6c9384
commit d4de1ac196
6 changed files with 608 additions and 110 deletions

View File

@@ -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",

View File

@@ -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) {

View File

@@ -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"})
}
}
})
});

View File

@@ -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()) {

View File

@@ -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>

View File

@@ -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"))