chore: update Discord-video-stream subproject to latest commit

This commit is contained in:
MythEclipse
2026-05-17 04:52:20 +07:00
parent 518577d79d
commit 71889ab689
15 changed files with 268 additions and 19 deletions
+1
View File
@@ -234,6 +234,7 @@ export default function App() {
onStartScreen={(source) => media.enqueue(source, "screen")}
onSkip={media.skip}
onStop={media.stop}
onVolumeChange={media.setVolume}
/>
)}
</TabsContent>
+7
View File
@@ -19,3 +19,10 @@ export function skipMedia(): Promise<MediaState> {
export function stopMedia(): Promise<MediaState> {
return request<MediaState>('/api/media/stop', { method: 'POST' });
}
export function setMediaVolume(volume: number): Promise<MediaState> {
return request<MediaState>('/api/media/volume', {
method: 'POST',
body: JSON.stringify({ volume }),
});
}
+18 -2
View File
@@ -11,9 +11,18 @@ interface MediaPanelProps {
onStartScreen: (source: string) => void;
onSkip: () => void;
onStop: () => void;
onVolumeChange: (volume: number) => void;
}
export function MediaPanel({ state, loading, onQueueMusic, onStartScreen, onSkip, onStop }: MediaPanelProps) {
export function MediaPanel({
state,
loading,
onQueueMusic,
onStartScreen,
onSkip,
onStop,
onVolumeChange,
}: MediaPanelProps) {
return (
<div className="grid gap-6 xl:grid-cols-[1fr_380px]">
<Tabs defaultValue="music" className="min-w-0">
@@ -22,7 +31,14 @@ export function MediaPanel({ state, loading, onQueueMusic, onStartScreen, onSkip
<TabsTrigger value="screen">Screen Share</TabsTrigger>
</TabsList>
<TabsContent value="music">
<MusicPlayer loading={loading} onQueue={onQueueMusic} onSkip={onSkip} onStop={onStop} />
<MusicPlayer
loading={loading}
volume={state.musicVolume}
onVolumeChange={onVolumeChange}
onQueue={onQueueMusic}
onSkip={onSkip}
onStop={onStop}
/>
</TabsContent>
<TabsContent value="screen">
<ScreenShare loading={loading} onStart={onStartScreen} onSkip={onSkip} onStop={onStop} />
+41 -2
View File
@@ -1,18 +1,42 @@
import { Music2 } from "lucide-react";
import { useState } from "react";
import { useEffect, useState } from "react";
import { Button } from "../ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card";
import { Input } from "../ui/input";
interface MusicPlayerProps {
loading: boolean;
volume: number;
onVolumeChange: (volume: number) => void;
onQueue: (source: string) => void;
onSkip: () => void;
onStop: () => void;
}
export function MusicPlayer({ loading, onQueue, onSkip, onStop }: MusicPlayerProps) {
export function MusicPlayer({
loading,
volume,
onVolumeChange,
onQueue,
onSkip,
onStop,
}: MusicPlayerProps) {
const [source, setSource] = useState("");
const safeVolume = Number.isFinite(volume) ? Math.max(0, Math.min(1, volume)) : 1;
const [draftVolume, setDraftVolume] = useState(Math.round(safeVolume * 100));
useEffect(() => {
setDraftVolume(Math.round(safeVolume * 100));
}, [safeVolume]);
useEffect(() => {
const normalized = draftVolume / 100;
if (Math.abs(normalized - safeVolume) < 0.001) return;
const timer = window.setTimeout(() => {
onVolumeChange(normalized);
}, 150);
return () => window.clearTimeout(timer);
}, [draftVolume, onVolumeChange, safeVolume]);
const submit = () => {
const trimmed = source.trim();
@@ -34,6 +58,21 @@ export function MusicPlayer({ loading, onQueue, onSkip, onStop }: MusicPlayerPro
onKeyDown={(event) => event.key === "Enter" && submit()}
placeholder="YouTube URL, Spotify track, or search terms"
/>
<div className="space-y-2">
<div className="flex items-center justify-between text-sm">
<span className="font-medium">Volume</span>
<span className="text-muted-foreground">{draftVolume}%</span>
</div>
<input
type="range"
min={0}
max={100}
step={1}
value={draftVolume}
onChange={(event) => setDraftVolume(Number(event.target.value))}
className="h-2 w-full cursor-pointer accent-primary"
/>
</div>
<div className="flex flex-wrap gap-2">
<Button disabled={loading || !source.trim()} onClick={submit}>Queue / Play</Button>
<Button variant="secondary" disabled={loading} onClick={onSkip}>Skip</Button>
+37 -3
View File
@@ -1,8 +1,19 @@
import { useCallback, useEffect, useState } from "react";
import { getMediaStatus, queueMedia, skipMedia, stopMedia } from "../api/media";
import {
getMediaStatus,
queueMedia,
setMediaVolume,
skipMedia,
stopMedia,
} from "../api/media";
import type { MediaMode, MediaState } from "../types/media";
const emptyMediaState: MediaState = { playing: false, current: null, queue: [] };
const emptyMediaState: MediaState = {
playing: false,
musicVolume: 1,
current: null,
queue: [],
};
export function useMediaControl() {
const [mediaState, setMediaState] = useState<MediaState>(emptyMediaState);
@@ -55,9 +66,32 @@ export function useMediaControl() {
}
}, []);
const setVolume = useCallback(async (volume: number) => {
setError(null);
try {
const state = await setMediaVolume(volume);
setMediaState(state);
return state;
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
setError(message);
throw err;
}
}, []);
useEffect(() => {
refreshMedia().catch((err) => setError(err instanceof Error ? err.message : String(err)));
}, [refreshMedia]);
return { mediaState, setMediaState, loading, error, refreshMedia, enqueue, skip, stop };
return {
mediaState,
setMediaState,
loading,
error,
refreshMedia,
enqueue,
skip,
stop,
setVolume,
};
}
+1
View File
@@ -11,6 +11,7 @@ export interface MediaItem {
export interface MediaState {
playing: boolean;
musicVolume: number;
current: MediaItem | null;
queue: MediaItem[];
}