feat: połączenie przycisków zapętlania w jeden przycisk z cyklem Wyłączone -> Kolejka -> Utwór (styl YT Music/Spotify)
This commit is contained in:
4 files changed
+80
-53
No files matched your search
@@ -599,19 +599,27 @@ class MusicBot(commands.Cog):
|
||||
ephemeral=True
|
||||
)
|
||||
|
||||
@app_commands.command(name="loop", description="Włącz/wyłącz zapętlanie aktualnego utworu")
|
||||
@app_commands.command(name="loop", description="Zmień tryb zapętlania (Wyłączone -> Cała kolejka -> Jeden utwór)")
|
||||
async def loop_command(self, interaction: discord.Interaction):
|
||||
"""Włącz/wyłącz zapętlanie aktualnego utworu"""
|
||||
"""Zmień tryb zapętlania jak w YT Music/Spotify"""
|
||||
guild_id = interaction.guild.id
|
||||
loop_current = not self.get_loop_current(guild_id)
|
||||
self.set_loop_current(guild_id, loop_current)
|
||||
if loop_current:
|
||||
loop_current = self.get_loop_current(guild_id)
|
||||
loop_pl = self.get_loop_playlist(guild_id)
|
||||
|
||||
if not loop_current and not loop_pl:
|
||||
self.set_loop_playlist(guild_id, True)
|
||||
self.set_loop_current(guild_id, False)
|
||||
msg = "🔁 Tryb zapętlania: **Cała kolejka (playlista)**"
|
||||
elif loop_pl and not loop_current:
|
||||
self.set_loop_playlist(guild_id, False)
|
||||
state = "włączone" if loop_current else "wyłączone"
|
||||
await interaction.response.send_message(
|
||||
f"Zapętlanie aktualnego utworu zostało {state}.",
|
||||
ephemeral=False
|
||||
)
|
||||
self.set_loop_current(guild_id, True)
|
||||
msg = "🔂 Tryb zapętlania: **Jeden utwór**"
|
||||
else:
|
||||
self.set_loop_playlist(guild_id, False)
|
||||
self.set_loop_current(guild_id, False)
|
||||
msg = "➡️ Tryb zapętlania: **Wyłączone**"
|
||||
|
||||
await interaction.response.send_message(msg, ephemeral=False)
|
||||
|
||||
@app_commands.command(name="loopplaylist", description="Włącz/wyłącz zapętlanie całej kolejki (tryb playlisty)")
|
||||
async def loopplaylist_command(self, interaction: discord.Interaction):
|
||||
|
||||
+32
-39
@@ -31,7 +31,6 @@ const playPauseIcon = document.getElementById('play-pause-icon');
|
||||
const btnSkip = document.getElementById('btn-skip');
|
||||
const btnStop = document.getElementById('btn-stop');
|
||||
const btnLoop = document.getElementById('btn-loop');
|
||||
const btnLoopPlaylist = document.getElementById('btn-loop-playlist');
|
||||
const vinylRecord = document.getElementById('vinyl-record');
|
||||
const musicWave = document.getElementById('music-wave');
|
||||
const volumeSlider = document.getElementById('volume-slider');
|
||||
@@ -142,9 +141,6 @@ function bindEvents() {
|
||||
btnSkip.addEventListener('click', skipTrack);
|
||||
btnStop.addEventListener('click', stopPlayback);
|
||||
btnLoop.addEventListener('click', toggleLoop);
|
||||
if (btnLoopPlaylist) {
|
||||
btnLoopPlaylist.addEventListener('click', toggleLoopPlaylist);
|
||||
}
|
||||
|
||||
// Volume slider
|
||||
volumeSlider.addEventListener('input', (e) => {
|
||||
@@ -375,28 +371,39 @@ function updateUI(status) {
|
||||
updateVolumeIcon(status.volume);
|
||||
}
|
||||
|
||||
// 5. Loop State
|
||||
if (status.loop_single) {
|
||||
loopBadge.style.display = 'flex';
|
||||
// 5. Loop State (Combined toggle: Off -> Playlist -> Single)
|
||||
let loopMode = 'off';
|
||||
if (status.loop_single) loopMode = 'single';
|
||||
else if (status.loop_playlist) loopMode = 'playlist';
|
||||
|
||||
if (btnLoop.dataset.loopMode !== loopMode) {
|
||||
btnLoop.dataset.loopMode = loopMode;
|
||||
if (loopMode === 'single') {
|
||||
btnLoop.innerHTML = '<i data-lucide="repeat-1"></i>';
|
||||
btnLoop.classList.add('active');
|
||||
btnLoop.style.background = 'var(--primary)';
|
||||
btnLoop.style.color = 'var(--bg-card)';
|
||||
btnLoop.title = "Zapętlenie: jeden utwór (kliknij, aby wyłączyć)";
|
||||
loopBadge.style.display = 'flex';
|
||||
loopBadge.innerHTML = '<i data-lucide="repeat-1"></i> Zapętlenie utworu';
|
||||
} else if (loopMode === 'playlist') {
|
||||
btnLoop.innerHTML = '<i data-lucide="repeat"></i>';
|
||||
btnLoop.classList.add('active');
|
||||
btnLoop.style.background = 'var(--primary)';
|
||||
btnLoop.style.color = 'var(--bg-card)';
|
||||
btnLoop.title = "Zapętlenie: cała kolejka (kliknij, aby zapętlić jeden utwór)";
|
||||
loopBadge.style.display = 'flex';
|
||||
loopBadge.innerHTML = '<i data-lucide="repeat"></i> Zapętlenie kolejki';
|
||||
} else {
|
||||
loopBadge.style.display = 'none';
|
||||
btnLoop.innerHTML = '<i data-lucide="repeat"></i>';
|
||||
btnLoop.classList.remove('active');
|
||||
btnLoop.style.background = '';
|
||||
btnLoop.style.color = '';
|
||||
btnLoop.title = "Zapętlenie: wyłączone (kliknij, aby zapętlić kolejkę)";
|
||||
loopBadge.style.display = 'none';
|
||||
}
|
||||
|
||||
if (btnLoopPlaylist) {
|
||||
if (status.loop_playlist) {
|
||||
btnLoopPlaylist.classList.add('active');
|
||||
btnLoopPlaylist.style.background = 'var(--primary)';
|
||||
btnLoopPlaylist.style.color = 'var(--bg-card)';
|
||||
} else {
|
||||
btnLoopPlaylist.classList.remove('active');
|
||||
btnLoopPlaylist.style.background = '';
|
||||
btnLoopPlaylist.style.color = '';
|
||||
if (window.lucide) {
|
||||
lucide.createIcons();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,35 +707,21 @@ async function stopPlayback() {
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle Loop State
|
||||
// Toggle Loop State (Cycle: Off -> Playlist -> Single -> Off)
|
||||
async function toggleLoop() {
|
||||
if (!currentStatus) return;
|
||||
const nextState = !currentStatus.loop_single;
|
||||
try {
|
||||
const res = await fetch('/api/loop', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ loop: nextState })
|
||||
});
|
||||
if (!res.ok) throw new Error('Loop failed');
|
||||
showToast(nextState ? 'Zapętlanie utworu włączone' : 'Zapętlanie utworu wyłączone', 'info');
|
||||
const res = await fetch('/api/loop_cycle', { method: 'POST' });
|
||||
if (!res.ok) throw new Error('Loop cycle failed');
|
||||
const data = await res.json();
|
||||
if (data.mode === 'playlist') showToast('Zapętlanie kolejki włączone', 'info');
|
||||
else if (data.mode === 'single') showToast('Zapętlanie utworu włączone', 'info');
|
||||
else showToast('Zapętlanie wyłączone', 'info');
|
||||
} catch (err) {
|
||||
showToast('Błąd ustawiania pętli', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle Playlist Loop State
|
||||
async function toggleLoopPlaylist() {
|
||||
try {
|
||||
const res = await fetch('/api/loop_playlist', { method: 'POST' });
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.detail || 'Błąd zapętlania');
|
||||
showToast(data.loop_playlist ? 'Włączono zapętlanie całej kolejki (playlisty)!' : 'Wyłączono zapętlanie kolejki.', 'info');
|
||||
} catch (err) {
|
||||
showToast('Nie udało się zmienić trybu zapętlania', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Set Volume
|
||||
async function setVolume(vol) {
|
||||
try {
|
||||
|
||||
+1
-4
@@ -112,10 +112,7 @@
|
||||
<button class="btn-icon btn-secondary" id="btn-skip" title="Pomiń utwór">
|
||||
<i data-lucide="skip-forward"></i>
|
||||
</button>
|
||||
<button class="btn-icon btn-secondary" id="btn-loop" title="Zapętl ten utwór">
|
||||
<i data-lucide="repeat-1"></i>
|
||||
</button>
|
||||
<button class="btn-icon btn-secondary" id="btn-loop-playlist" title="Zapętlij całą kolejkę (tryb playlisty)">
|
||||
<button class="btn-icon btn-secondary" id="btn-loop" title="Zapętlanie: wyłączone">
|
||||
<i data-lucide="repeat"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -402,6 +402,35 @@ async def api_loop_playlist():
|
||||
await broadcast_status()
|
||||
return {"status": "success", "loop_playlist": new_state}
|
||||
|
||||
@app.post("/api/loop_cycle")
|
||||
async def api_loop_cycle():
|
||||
music_cog = bot.get_cog("MusicBot")
|
||||
if not music_cog:
|
||||
raise HTTPException(status_code=500, detail="MusicBot cog not loaded")
|
||||
|
||||
guild = get_active_guild()
|
||||
if not guild:
|
||||
raise HTTPException(status_code=400, detail="Bot has no active server context")
|
||||
|
||||
loop_current = music_cog.get_loop_current(guild.id)
|
||||
loop_pl = music_cog.get_loop_playlist(guild.id)
|
||||
|
||||
if not loop_current and not loop_pl:
|
||||
music_cog.set_loop_playlist(guild.id, True)
|
||||
music_cog.set_loop_current(guild.id, False)
|
||||
mode = "playlist"
|
||||
elif loop_pl and not loop_current:
|
||||
music_cog.set_loop_playlist(guild.id, False)
|
||||
music_cog.set_loop_current(guild.id, True)
|
||||
mode = "single"
|
||||
else:
|
||||
music_cog.set_loop_playlist(guild.id, False)
|
||||
music_cog.set_loop_current(guild.id, False)
|
||||
mode = "off"
|
||||
|
||||
await broadcast_status()
|
||||
return {"status": "success", "mode": mode}
|
||||
|
||||
@app.post("/api/remove")
|
||||
async def api_remove(req: RemoveRequest):
|
||||
music_cog = bot.get_cog("MusicBot")
|
||||
|
||||
Reference in new issue
Block a user