Fix: Watch Together Raum-Erstellung + Download-Button

- Server/Frontend Protokoll-Mismatch behoben (create_room, join_room, room_created, room_joined, playback_state)
- togglePlay sendet jetzt korrekt pause/resume statt toggle_play
- Download-Button: SPA-Fallback fuer /downloads/ verhindert, 404 statt Reload
- download-Attribut am Link hinzugefuegt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Daniel 2026-03-07 02:48:43 +01:00
parent 73f247ada3
commit e748fc97e9
6 changed files with 74 additions and 61 deletions

View file

@ -153,6 +153,14 @@ async function boot(): Promise<void> {
} }
} }
// Serve download files (Electron installer etc.)
const downloadsDir = path.join(import.meta.dirname ?? __dirname, '..', '..', 'downloads');
app.use('/downloads', express.static(downloadsDir));
// 404 for missing downloads (prevent SPA fallback)
app.get('/downloads/{*splat}', (_req, res) => {
res.status(404).json({ error: 'Download not found' });
});
// SPA Fallback (MUST be after plugin routes) // SPA Fallback (MUST be after plugin routes)
app.get('/{*splat}', (_req, res) => { app.get('/{*splat}', (_req, res) => {
res.sendFile(path.join(import.meta.dirname ?? __dirname, '..', '..', 'web', 'dist', 'index.html')); res.sendFile(path.join(import.meta.dirname ?? __dirname, '..', '..', 'web', 'dist', 'index.html'));

View file

@ -75,8 +75,7 @@ function getPlaybackState(room: Room): Record<string, any> {
const currentTime = room.currentTime + (room.playing ? (Date.now() - room.lastSyncAt) / 1000 : 0); const currentTime = room.currentTime + (room.playing ? (Date.now() - room.lastSyncAt) / 1000 : 0);
return { return {
type: 'playback_state', type: 'playback_state',
videoUrl: room.currentVideo?.url ?? null, currentVideo: room.currentVideo ? { url: room.currentVideo.url, title: room.currentVideo.title } : null,
videoTitle: room.currentVideo?.title ?? null,
playing: room.playing, playing: room.playing,
currentTime, currentTime,
updatedAt: Date.now(), updatedAt: Date.now(),

File diff suppressed because one or more lines are too long

2
web/dist/index.html vendored
View file

@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Gaming Hub</title> <title>Gaming Hub</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎮</text></svg>" /> <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎮</text></svg>" />
<script type="module" crossorigin src="/assets/index-ZMOZU_VE.js"></script> <script type="module" crossorigin src="/assets/index-CgrjD6IO.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-C2eno-Si.css"> <link rel="stylesheet" crossorigin href="/assets/index-C2eno-Si.css">
</head> </head>
<body> <body>

View file

@ -134,6 +134,7 @@ export default function App() {
<a <a
className="hub-download-btn" className="hub-download-btn"
href="/downloads/GamingHub-Setup.exe" href="/downloads/GamingHub-Setup.exe"
download
title="Desktop App herunterladen" title="Desktop App herunterladen"
> >
{'\u2B07\uFE0F'} {'\u2B07\uFE0F'}

View file

@ -263,35 +263,38 @@ export default function WatchTogetherTab({ data }: { data: any }) {
if (msg.rooms) setRooms(msg.rooms); if (msg.rooms) setRooms(msg.rooms);
break; break;
case 'room_created': case 'room_created': {
const r = msg.room;
setCurrentRoom({ setCurrentRoom({
id: msg.roomId, id: r.id,
name: msg.name, name: r.name,
hostId: msg.hostId, hostId: r.hostId,
members: msg.members || [], members: r.members || [],
currentVideo: null, currentVideo: r.currentVideo || null,
playing: false, playing: r.playing || false,
currentTime: 0, currentTime: r.currentTime || 0,
queue: [], queue: r.queue || [],
}); });
break; break;
}
case 'room_joined': case 'room_joined': {
const r = msg.room;
setCurrentRoom({ setCurrentRoom({
id: msg.roomId, id: r.id,
name: msg.name, name: r.name,
hostId: msg.hostId, hostId: r.hostId,
members: msg.members || [], members: r.members || [],
currentVideo: msg.currentVideo || null, currentVideo: r.currentVideo || null,
playing: msg.playing || false, playing: r.playing || false,
currentTime: msg.currentTime || 0, currentTime: r.currentTime || 0,
queue: msg.queue || [], queue: r.queue || [],
}); });
// Load video if one is playing if (r.currentVideo?.url) {
if (msg.currentVideo?.url) { setTimeout(() => loadVideo(r.currentVideo.url), 100);
setTimeout(() => loadVideo(msg.currentVideo.url), 100);
} }
break; break;
}
case 'playback_state': { case 'playback_state': {
const room = currentRoomRef.current; const room = currentRoomRef.current;
@ -402,8 +405,8 @@ export default function WatchTogetherTab({ data }: { data: any }) {
if (wsRef.current?.readyState === WebSocket.OPEN) { if (wsRef.current?.readyState === WebSocket.OPEN) {
wsSend({ wsSend({
type: 'create_room', type: 'create_room',
name: userName.trim(), name: roomName.trim(),
roomName: roomName.trim(), userName: userName.trim(),
password: roomPassword.trim() || undefined, password: roomPassword.trim() || undefined,
}); });
} else { } else {
@ -423,7 +426,7 @@ export default function WatchTogetherTab({ data }: { data: any }) {
if (wsRef.current?.readyState === WebSocket.OPEN) { if (wsRef.current?.readyState === WebSocket.OPEN) {
wsSend({ wsSend({
type: 'join_room', type: 'join_room',
name: userName.trim(), userName: userName.trim(),
roomId, roomId,
password: password?.trim() || undefined, password: password?.trim() || undefined,
}); });
@ -458,7 +461,9 @@ export default function WatchTogetherTab({ data }: { data: any }) {
// ── Playback controls (host only) ── // ── Playback controls (host only) ──
const togglePlay = useCallback(() => { const togglePlay = useCallback(() => {
wsSend({ type: 'toggle_play' }); const room = currentRoomRef.current;
if (!room) return;
wsSend({ type: room.playing ? 'pause' : 'resume' });
}, [wsSend]); }, [wsSend]);
const skip = useCallback(() => { const skip = useCallback(() => {