soundboard
All checks were successful
/ upload (release) Successful in 27s

This commit is contained in:
pavel 2026-02-24 01:41:24 +01:00
commit 33da605c03
13 changed files with 534 additions and 9 deletions

View file

@ -86,6 +86,18 @@ const el = {
channelName: document.getElementById("channel-name"),
channelModalCancel: document.getElementById("channel-modal-cancel"),
// Sound Board
soundboard: document.getElementById('soundboard'),
soundboardGrid: document.getElementById('soundboard-grid'),
addSoundBtn: document.getElementById('add-sound-btn'),
soundModal: document.getElementById('sound-modal'),
soundForm: document.getElementById('sound-form'),
soundName: document.getElementById('sound-name'),
soundIcon: document.getElementById('sound-icon'),
soundFile: document.getElementById('sound-file'),
soundModalCancel: document.getElementById('sound-modal-cancel'),
soundSubmitBtn: document.getElementById('sound-submit-btn'),
// Mobile
mobileMenuBtn: document.getElementById("mobile-menu-btn"),
mobileMembersBtn: document.getElementById("mobile-members-btn"),
@ -850,8 +862,10 @@ async function joinVoice() {
const channel = state.channels.find(c => c.id === state.selectedVoiceChannelId);
el.vcChannelName.textContent = channel ? channel.name : "Voice";
el.voiceConnection.classList.remove("hidden");
el.soundboard.classList.remove("hidden");
playSound('join');
refreshVoicePresence().catch(() => { });
loadSounds().catch(() => { });
};
ws.onmessage = async (event) => {
@ -890,12 +904,16 @@ async function joinVoice() {
if (videoEl) {
videoEl.parentElement.classList.toggle('speaking', msg.is_speaking);
}
} else if (msg.type === "play_sound") {
const audio = new Audio(msg.sound_url);
audio.play().catch(console.error);
}
refreshVoicePresence().catch(() => { });
};
ws.onclose = () => {
el.voiceConnection.classList.add("hidden");
el.soundboard.classList.add("hidden");
for (const pc of state.voice.peerConnections.values()) pc.close();
state.voice.peerConnections.clear();
stopAndClearAudioPipeline();
@ -1014,6 +1032,41 @@ function toggleWatchVideo() {
lucide.createIcons();
}
// --- Sound Board ---
async function loadSounds() {
if (!state.selectedGuildId) return;
try {
const sounds = await api(`/guilds/${state.selectedGuildId}/sounds`);
renderSounds(sounds);
} catch (err) {
console.error("failed to load sounds", err);
}
}
function renderSounds(sounds) {
el.soundboardGrid.innerHTML = '';
sounds.forEach(sound => {
const item = document.createElement('div');
item.className = 'sound-item';
item.innerHTML = `
<div class="sound-icon">${sound.icon}</div>
<div class="sound-name">${sound.name}</div>
`;
item.onclick = () => playRemoteSound(sound.file_path);
el.soundboardGrid.appendChild(item);
});
}
function playRemoteSound(url) {
if (state.voice.ws && state.voice.ws.readyState === WebSocket.OPEN) {
state.voice.ws.send(JSON.stringify({ type: 'play_sound', sound_url: url }));
}
// Also play locally immediately
const audio = new Audio(url);
audio.play().catch(console.error);
}
// --- Mobile Logic ---
function toggleMobileMenu() {
@ -1192,6 +1245,46 @@ async function init() {
el.voiceMuteBtn.onclick = toggleMute;
el.voiceLeaveBtn.onclick = leaveVoice;
el.addSoundBtn.onclick = () => {
el.soundModal.classList.remove("hidden");
};
el.soundModalCancel.onclick = () => {
el.soundModal.classList.add("hidden");
};
el.soundForm.onsubmit = async (e) => {
e.preventDefault();
if (!state.selectedGuildId) return;
const formData = new FormData();
formData.append('name', el.soundName.value);
formData.append('icon', el.soundIcon.value);
formData.append('file', el.soundFile.files[0]);
el.soundSubmitBtn.disabled = true;
el.soundSubmitBtn.textContent = 'Uploading...';
try {
// Need to use fetch directly because api() might not handle FormData correctly depending on implementation
const response = await fetch(`/guilds/${state.selectedGuildId}/sounds`, {
method: 'POST',
body: formData
});
if (!response.ok) {
const err = await response.json();
throw new Error(err.error || 'Upload failed');
}
el.soundModal.classList.add("hidden");
el.soundForm.reset();
await loadSounds();
} catch (err) {
alert(err.message);
} finally {
el.soundSubmitBtn.disabled = false;
el.soundSubmitBtn.textContent = 'Add Sound';
}
};
try {
state.me = await api("/me");
if (state.me && state.me.display_name) {