diff --git a/server/src/index.ts b/server/src/index.ts index af19a95..dd08caf 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -12,6 +12,7 @@ import { NoSubscriberBehavior, getVoiceConnection, type VoiceConnection, + type AudioResource, generateDependencyReport, entersState, VoiceConnectionStatus @@ -58,6 +59,8 @@ type GuildAudioState = { player: ReturnType; guildId: string; channelId: string; + currentResource?: AudioResource; + currentVolume: number; // 0..1 }; const guildAudioState = new Map(); @@ -197,7 +200,7 @@ app.get('/api/sounds', (req: Request, res: Response) => { .map((file) => ({ fileName: file, name: path.parse(file).name })) .filter((s) => (q ? s.name.toLowerCase().includes(q) : true)); - res.json(items); + res.json({ items, total: files.length }); }); app.get('/api/channels', (_req: Request, res: Response) => { @@ -227,7 +230,7 @@ app.post('/api/play', async (req: Request, res: Response) => { volume?: number; // 0..1 }; if (!soundName || !guildId || !channelId) return res.status(400).json({ error: 'soundName, guildId, channelId erforderlich' }); - const safeVolume = typeof volume === 'number' && Number.isFinite(volume) ? Math.max(0, Math.min(1, volume)) : 1; + const safeVolume = typeof volume === 'number' && Number.isFinite(volume) ? Math.max(0, Math.min(1, volume)) : state?.currentVolume ?? 1; const filePath = path.join(SOUNDS_DIR, `${soundName}.mp3`); if (!fs.existsSync(filePath)) return res.status(404).json({ error: 'Sound nicht gefunden' }); @@ -250,7 +253,7 @@ app.post('/api/play', async (req: Request, res: Response) => { }); const player = createAudioPlayer({ behaviors: { noSubscriber: NoSubscriberBehavior.Play } }); connection.subscribe(player); - state = { connection, player, guildId, channelId }; + state = { connection, player, guildId, channelId, currentVolume: 1 }; guildAudioState.set(guildId, state); // Connection State Logs @@ -297,7 +300,7 @@ app.post('/api/play', async (req: Request, res: Response) => { }); const player = createAudioPlayer({ behaviors: { noSubscriber: NoSubscriberBehavior.Play } }); connection.subscribe(player); - state = { connection, player, guildId, channelId }; + state = { connection, player, guildId, channelId, currentVolume: 1 }; guildAudioState.set(guildId, state); state.connection = await ensureConnectionReady(connection, channelId, guildId, guild); @@ -323,6 +326,8 @@ app.post('/api/play', async (req: Request, res: Response) => { } state.player.stop(); state.player.play(resource); + state.currentResource = resource; + state.currentVolume = safeVolume; console.log(`${new Date().toISOString()} | player.play() called for ${soundName}`); return res.json({ ok: true }); } catch (err: any) { diff --git a/web/src/App.tsx b/web/src/App.tsx index 38caa4d..f1dda88 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -4,6 +4,7 @@ import type { VoiceChannelInfo, Sound } from './types'; export default function App() { const [sounds, setSounds] = useState([]); + const [total, setTotal] = useState(0); const [channels, setChannels] = useState([]); const [query, setQuery] = useState(''); const [selected, setSelected] = useState(''); @@ -15,7 +16,8 @@ export default function App() { (async () => { try { const [s, c] = await Promise.all([fetchSounds(), fetchChannels()]); - setSounds(s); + setSounds(s.items); + setTotal(s.total); setChannels(c); if (c[0]) setSelected(`${c[0].guildId}:${c[0].channelId}`); } catch (e: any) { @@ -26,7 +28,8 @@ export default function App() { const interval = setInterval(async () => { try { const s = await fetchSounds(query); - setSounds(s); + setSounds(s.items); + setTotal(s.total); } catch {} }, 10000); return () => clearInterval(interval); @@ -101,6 +104,7 @@ export default function App() { ))} {filtered.length === 0 &&
Keine Sounds gefunden.
} +
Geladene Sounds: {total}
); } diff --git a/web/src/api.ts b/web/src/api.ts index 663def2..f6ab2fd 100644 --- a/web/src/api.ts +++ b/web/src/api.ts @@ -1,8 +1,8 @@ -import type { Sound, VoiceChannelInfo } from './types'; +import type { Sound, SoundsResponse, VoiceChannelInfo } from './types'; const API_BASE = import.meta.env.VITE_API_BASE_URL || '/api'; -export async function fetchSounds(q?: string): Promise { +export async function fetchSounds(q?: string): Promise { const url = new URL(`${API_BASE}/sounds`, window.location.origin); if (q) url.searchParams.set('q', q); const res = await fetch(url.toString()); diff --git a/web/src/styles.css b/web/src/styles.css index ea72064..13d267e 100644 --- a/web/src/styles.css +++ b/web/src/styles.css @@ -49,6 +49,12 @@ header p { opacity: .8; } .hint { opacity: .7; padding: 24px 0; } +.footer-info { + margin-top: 14px; + opacity: .8; + font-size: 14px; +} + diff --git a/web/src/types.ts b/web/src/types.ts index 03fa0a1..1ee73e5 100644 --- a/web/src/types.ts +++ b/web/src/types.ts @@ -3,6 +3,11 @@ export type Sound = { name: string; }; +export type SoundsResponse = { + items: Sound[]; + total: number; +}; + export type VoiceChannelInfo = { guildId: string; guildName: string;