feat(gateway): restore Discord GoLive screenshare (dulu pernah ada, hilang saat split microservices)

User: 'dulu sharescreen juga bisa'. Terbukti: commit d50ce86 (Mei 2026)
punya src/media/screenShareController.ts + vendor @dank074/discord-video-stream,
hilang saat rombak monolith -> microservices. Interface ScreenShareController
masih ada di mediaTypes.ts tapi implementasinya tidak.

Restore:
- dep @dank074/discord-video-stream@6.0.0 (npm, dibangun untuk
  discord.js-selfbot-v13 — cocok dengan stack gateway)
- mediaSource.getDirectVideoUrl (yt-dlp --get-url bestvideo+bestaudio)
- screenShareController.ts (BARU): Streamer(client) + prepareStream H264
  720p30 + playStream go-live; owner check via discordPlayer
- media.handler: mode:'screen' di media:queue -> screen path; status
  expose activeMode; stop matiin screen
- FE: tombol Screen di MusicPlayer + hook useMediaQueue({url, mode})

Verifikasi: gateway tsc PASS, FE tsc PASS, biome 0 error, next build PASS.
Nix build pending (dep native @lng2004/node-datachannel butuh pnpm rebuild).
This commit is contained in:
asepharyana
2026-08-01 11:36:40 +07:00
parent 189ab1c1f6
commit 891c1305f0
9 changed files with 5460 additions and 6 deletions
@@ -29,15 +29,19 @@ export function MusicPlayer({ ws }: MusicPlayerProps) {
const stopMut = useMediaStop();
const volumeMut = useMediaVolume();
const [queueUrl, setQueueUrl] = useState("");
const [screenMode, setScreenMode] = useState(false);
// Sync WS media_state into the query cache
useMediaWsSync(ws);
const handleQueue = useCallback(() => {
if (!queueUrl.trim()) return;
queueMut.mutate(queueUrl.trim());
queueMut.mutate({
url: queueUrl.trim(),
mode: screenMode ? "screen" : "music",
});
setQueueUrl("");
}, [queueUrl, queueMut]);
}, [queueUrl, queueMut, screenMode]);
const handleVolume = useCallback(
(value: number | readonly number[]) => {
@@ -64,6 +68,15 @@ export function MusicPlayer({ ws }: MusicPlayerProps) {
onKeyDown={(e) => e.key === "Enter" && handleQueue()}
className="flex-1 h-9"
/>
<Button
variant={screenMode ? "default" : "secondary"}
size="sm"
onClick={() => setScreenMode((v) => !v)}
title="Queue as Discord GoLive screenshare instead of audio playback"
className="h-9"
>
Screen
</Button>
<Button
onClick={handleQueue}
disabled={!queueUrl.trim() || queueMut.isPending}
@@ -73,6 +86,14 @@ export function MusicPlayer({ ws }: MusicPlayerProps) {
</Button>
</div>
{mediaState?.activeMode && (
<p className="text-[10px] font-mono text-primary/80 uppercase tracking-wider">
{mediaState.activeMode === "screen"
? "Screen share active"
: "Music playing"}
</p>
)}
{mediaState?.current ? (
<div className="rounded-lg bg-gradient-to-br from-primary/5 to-primary/[0.02] border border-primary/10 p-4 space-y-2">
<p className="text-xs text-muted-foreground font-medium uppercase tracking-wider flex items-center gap-1.5">
+4 -1
View File
@@ -24,7 +24,10 @@ function useMediaAction<TArgs>(fn: (args: TArgs) => Promise<MediaState>) {
}
export function useMediaQueue() {
return useMediaAction((url: string) => mediaApi.queue(url, "music"));
return useMediaAction(
(input: { url: string; mode?: "music" | "screen" }) =>
mediaApi.queue(input.url, input.mode ?? "music"),
);
}
export function useMediaSkip() {
+1
View File
@@ -11,6 +11,7 @@ export interface MediaItem {
export interface MediaState {
playing: boolean;
activeMode?: MediaMode | null;
musicVolume: number;
current: MediaItem | null;
queue: MediaItem[];