Commit graph

43 commits

Author SHA1 Message Date
Daniel
c9378f4cdb Fix: Tab-Auswahl bleibt nach Reload erhalten (localStorage)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 01:10:01 +01:00
Daniel
4aed4e70ab fix(streaming): reliable disconnect + mandatory stream password
Disconnect:
- Server-side heartbeat ping/pong every 10s with 25s timeout
- Detects and cleans up dead connections (browser closed, network lost)
- ws.terminate() on heartbeat timeout triggers handleDisconnect

Password:
- Stream password is mandatory (server rejects start_broadcast without)
- Password stored server-side, never sent to clients
- Viewers must enter password via modal before joining
- Lock icon on tiles, WRONG_PASSWORD error shown in modal

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 01:00:48 +01:00
Daniel
dacfde4328 fix(streaming): resolve stale closure preventing remote WebRTC connections
The WebSocket onmessage handler captured isBroadcasting=false at creation
time. When ICE candidates arrived from remote viewers, the handler looked
up viewerPcRef instead of peerConnectionsRef, dropping all candidates.

Fix: use refs (isBroadcastingRef, viewingRef, handleWsMessageRef) so the
WS handler always reads current state. connectWs() now has [] deps and
delegates to handleWsMessageRef.current for every message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:51:09 +01:00
Daniel
29bcf67121 feat(streaming): add Screen Streaming plugin with WebRTC
New plugin: browser-based screen sharing via Chrome Screen Capture API.
Multi-stream grid layout (Rustdesk-style tiles) with live previews.

- Server: WebSocket signaling at /ws/streaming (SDP/ICE relay)
- Server: http.createServer for WebSocket attachment
- Frontend: StreamingTab with broadcaster/viewer modes
- Frontend: tile grid, fullscreen viewer, LIVE badges
- Supports multiple concurrent streams
- Peer-to-peer video via WebRTC (no video through server)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:39:49 +01:00
Daniel
9ff8a38547 feat(soundboard): download modal with filename input + fix yt-dlp binary
- Add download modal: filename input, progress phases (input/downloading/done/error)
- Refactor backend: shared handleUrlDownload() with optional custom filename + rename
- Fix Dockerfile: use yt-dlp_linux standalone binary (no Python dependency)
- Modal shows URL type badge (YouTube/Instagram/MP3), spinner, retry on error

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:59:31 +01:00
Daniel
df937f3e40 fix(soundboard): auto-prepend https:// for URL import
URLs pasted without protocol (e.g. instagram.com/reel/...) now work.
normalizeUrl() adds https:// if missing. Input type changed from
"url" to "text" to prevent browser validation blocking paste.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:45:41 +01:00
Daniel
200f03c1f8 feat(soundboard): extend URL download to support YouTube & Instagram
yt-dlp extracts audio as MP3 from YouTube and Instagram links.
Direct MP3 links continue to work as before. URL input field now shows
a type indicator (YT/IG/MP3) and validates all three formats.

Backend: downloadWithYtDlp() spawns yt-dlp with --extract-audio,
saves to SOUNDS_DIR, normalizes if enabled. New /download-url route
for save-only without auto-play. play-url route extended for all types.

Frontend: isSupportedUrl() validates YouTube/Instagram/MP3, dynamic
icon changes per URL type, disabled state when URL is unsupported.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:38:09 +01:00
Daniel
7786d02f86 fix: globe clickable after tab switch — deferred init with ResizeObserver
Globe.gl needs non-zero container dimensions for initialization and click
handling. With the tab persistence fix (display:none for hidden tabs), the
globe container starts at 0×0 when radio isn't the first tab. Added a
separate ResizeObserver that detects when the container becomes visible
and triggers globe initialization via containerVisible state dependency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:17:48 +01:00
Daniel
056024d753 fix: preserve Radio tab play state across tab switches
All tab components are now always mounted and hidden via CSS
(display: none/contents) instead of conditionally rendered.
This prevents React state from being destroyed on tab switch,
so nowPlaying, SSE handlers, and stop buttons keep working.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:04:49 +01:00
Daniel
87279933c3 refactor(lolstats): switch from MCP to op.gg REST API for fresh data
- Profile now fetched via REST API (summoner lookup + summary endpoint)
- Match history via REST API games endpoint (proper JSON, no parser)
- All 10 players per game returned directly (no separate detail fetch)
- DDragon champion ID→name mapping loaded at startup
- Fixed summoner_id lookup to use # separator (was using - which failed)
- MCP kept as fallback for match detail and edge cases
- Frontend: find "me" by summoner name instead of assuming index 0

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:41:48 +01:00
Daniel
f4c8cce2f9 feat(lolstats): add data renewal — auto-refresh stale op.gg data
Uses op.gg REST API to trigger summoner data renewal before fetching
stats via MCP. Adds Update button in profile header for manual refresh.
Flow: lookup summoner_id → POST renewal → poll until finish → re-fetch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:22:32 +01:00
Daniel
40c596fbfa feat: add LoL Stats plugin — op.gg-powered player lookup
New plugin for League of Legends stats tracking, similar to op.gg:
- Search summoners by Riot ID (Name#Tag) + region
- Profile overview: rank, tier, LP, win rate, ladder position
- Top champions with KDA and win rates
- Match history with KDA, CS, items, game duration
- Expandable match details showing all 10 players
- Recent searches persisted across restarts

Uses op.gg MCP server (no API key needed, no 24h expiration).
Backend: server/src/plugins/lolstats/ (3 files)
Frontend: web/src/plugins/lolstats/ (2 files)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 21:04:45 +01:00
Daniel
fcf4ba86ce chore: globe texture q90 statt q85
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:13:53 +01:00
Daniel
5793a8f358 Optimize: Resize NASA Blue Marble from 28MB to 1.1MB
Original was 21600x10800 (full NASA resolution) - way too large
for a WebGL texture. Resized to 4096x2048 (max useful for globe.gl)
at JPEG quality 85 with progressive loading.

28 MB → 1.1 MB = 96% smaller, dramatically faster initial load.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:10:02 +01:00
Daniel
dd71d763cd Feat: Show member count in soundboard channel dropdown
Add voice channel member count (non-bot users) to soundboard
channel API response and display it in the dropdown, matching
the radio plugin's existing behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 20:02:00 +01:00
Daniel
262cc9f213 Fix: Radio sidebar close + topbar layout consistency
1. FAB button (favorites star) now hidden when any panel is open.
   Previously the FAB (z-index:20) covered the panel close button
   (z-index:15), making it impossible to close the station sidebar.

2. Added margin-left:auto to radio-topbar-right so theme dots
   always stay right-aligned. Previously they sat left when no
   station was playing (missing flex:1 now-playing spacer).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:51:04 +01:00
Daniel
b4f3c7db4d Fix: Stop globe rotation while station panel is open
Auto-rotation now stops completely when a station list or
favorites sidebar is visible. Resumes only when all panels
are closed. Interaction-based pause (drag/scroll) also
respects open panels - won't resume after 5s timeout if
a sidebar is still showing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:40:35 +01:00
Daniel
1b2fbe27ed Fix: Size-based dot radius for radio stations
Restore d.size-dependent point radius so larger cities (more
stations) show bigger dots like Radio Garden. Formula:
radius = clamp(0.12, 0.45, 0.06 + size * 0.005)

Zoom scaling also respects per-station size.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 19:37:34 +01:00
Daniel
a923463f83 Fix: Crisp radio station dots with zoom-scaling
Replace sprite-based markers (objectsData + SpriteMaterial with
soft 64x64 gradient texture → blurry at zoom) with optimized
point meshes:

- pointResolution(24): smooth 24-sided circles (no hexagons)
- pointAltitude(0.001): nearly flat on surface (no cylinder effect)
- sqrt-based zoom scaling: dots shrink when zooming in, grow when
  zooming out → visually consistent at all zoom levels
- Removed three.js Sprite/SpriteMaterial/CanvasTexture imports

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:30:42 +01:00
Daniel
99421f4577 Fix radio marker object placement 2026-03-06 16:15:44 +01:00
Daniel
3d59eda3da Fix radio marker sprites and size scaling 2026-03-06 14:08:09 +01:00
Daniel
d55aaf71b1 Render radio stations as sprite particles 2026-03-06 13:58:50 +01:00
Daniel
693f719abc Switch radio globe imagery to NASA Blue Marble 2026-03-06 12:10:19 +01:00
Daniel
54a53a98b7 Improve radio globe tile sharpness 2026-03-06 12:01:41 +01:00
Daniel
b8268b4999 Fix radio globe texture rendering 2026-03-06 11:55:26 +01:00
Daniel
f18638ff57 Refactor: Radio topbar layout + fix soundboard connection modal
- Radio: Move controls from bottom bar to topbar above globe
  (guild/channel selectors, now playing, volume, connection, stop)
- Radio: Theme selector moved inline into topbar-right
- Radio: Globe now fills remaining space below topbar
- Soundboard: Fix connection modal not opening when voiceStats is null
  (modal now renders with '---' values when no voice data available)
- Both: Modal condition changed from `showConnModal && voiceStats` to
  just `showConnModal`, handling null voiceStats gracefully

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:36:01 +01:00
Daniel
63afc55836 Fix: Tile proxy for black globe + radio voicestats modal
- Globe was black because Radio Garden CDN (rg-tiles.b-cdn.net) returns
  403 without Referer: radio.garden header. Added server-side tile proxy
  /api/radio/tile/:z/:x/:y with in-memory cache (max 500 tiles).
- Added radio_voicestats SSE broadcast (every 5s) with voice ping,
  gateway ping, status, channel name, and connected-since timestamp.
- Added clickable "Verbunden" connection indicator in RadioTab bottom
  bar with live ping display and connection details modal (matching
  soundboard's existing modal pattern).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:19:19 +01:00
Daniel
fd0a95be8e Fix: Add @types/three for TypeScript build
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:44:19 +01:00
Daniel
ed7e07a9ba Feat: Dynamic tile-based globe texture (like Radio Garden)
Replace static earth texture with tile-based rendering from CDN.
Tiles load progressively (zoom 1→2→3 base), then dynamically
load zoom 4-6 as user zooms in. Mercator→equirectangular
reprojection via strip-based canvas compositing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:43:49 +01:00
Daniel
e0635b30ef Fix: Radio search results empty + texture quality
- Search API: read title/subtitle/url from _source.page (nested)
- Channel click: extract correct ID from URL (last segment)
- Replace earth texture with higher-res 4096x2048 original

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:00:44 +01:00
Daniel
a1a49f0ec3 fix: host NASA Earth texture locally to avoid CORS
NASA's image server blocks cross-origin requests. Downloaded the
3600x1800 Black Marble texture and serve it from web/public/.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 09:50:29 +01:00
Daniel
a9f81d66fd fix: sharp globe rendering - HiDPI pixel ratio + NASA 4K texture
- Set renderer pixel ratio to window.devicePixelRatio for crisp
  rendering on Retina/HiDPI displays (was defaulting to 1)
- Upgraded Earth texture from low-res three-globe example (~2K)
  to NASA Black Marble 3600x1800 for sharp detail when zooming
- Added pointResolution(6) for cleaner point geometry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 09:46:18 +01:00
Daniel
8c2d75380d fix: smooth globe rotation - only rescale points on zoom change
pointRadius was recalculated on every controls change event (including
rotation frames), causing 50k+ points to re-render each frame.
Now only triggers when altitude changes >5%, keeping rotation smooth.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 09:44:24 +01:00
Daniel
2c0cb7a67a fix: volume slider disappearing + theme overlapping favorites
- SSE handler now distinguishes per-guild events (single NowPlaying)
  from snapshots (Record<string, NowPlaying>) to prevent state corruption
- Theme selector moved left (right: 72px) to not overlap favorites button

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 09:39:27 +01:00
Daniel
bac2e3b17f fix: scale radio globe points with zoom level
Points now shrink proportionally when zooming in, preventing
overlap in dense areas and making individual stations clickable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 09:35:48 +01:00
Daniel
90ef17932c feat: Radio themes + globe rotation pause on interaction
- 5 themes: Sunset (default), Midnight, Forest, Ocean, Cherry
- Theme selector dots in top-right corner, persisted to localStorage
- Globe accent colors (points, atmosphere) update with theme
- Globe pauses auto-rotation for 5s on any interaction (click, drag, scroll)
- All radio CSS vars scoped to .radio-container[data-theme]

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 02:15:06 +01:00
Daniel
d1ae2db00b feat: Radio volume control - server-wide slider synced via SSE
- Server: inlineVolume on AudioResource, POST /api/radio/volume endpoint
- Volume persisted per guild, broadcast via SSE to all clients
- Frontend: volume slider in bottom bar with debounced API calls
- Volume icon changes based on level (muted/low/normal)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 02:10:02 +01:00
Daniel
c41138f62a fix: stop globe auto-rotation on point click
Rotation continued while browsing stations, making selection impossible.
Now stops immediately when clicking any radio point.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:18:35 +01:00
Daniel
1d4a48cf74 fix: useRef requires initial value in strict TS mode
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 01:00:37 +01:00
Daniel
901dfe54be feat: Soundboard Plugin + Radio Globe Fixes
- Add Soundboard plugin (full Jukebox port): server + frontend + CSS
- Fix Radio Globe: swap geo coords (API returns [lng,lat] not [lat,lng])
- Fix Radio stations showing "Unbekannt": use item.page.title + fix channel ID regex
- Add DirectMessages + MessageContent intents for DM commands
- Register SoundboardTab in App.tsx with scoped theme/card-size CSS vars

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 00:51:07 +01:00
Daniel
db55d41eee fix: TypeScript-Fehler in RadioTab (useRef + Globe Konstruktor)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:27:14 +01:00
Daniel
847c963d86 feat: Radio Plugin – 3D Globe mit weltweiten Radiosendern
- Radio Garden API Client (30K+ Orte, Sender-Suche, Stream-URL Auflösung)
- Discord Voice Streaming via ffmpeg (PCM Pipeline)
- Interactive 3D Globe (globe.gl) mit allen Radiosender-Standorten
- Sender-Panel mit Play/Stop/Favoriten
- Live-Suche nach Sendern und Städten
- Now-Playing Bar mit Equalizer-Animation
- Guild/Voice-Channel Auswahl
- SSE Broadcasting für Live-Updates
- Favoriten-System mit Persistenz
- Responsive Design (Mobile/Tablet/Desktop)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:23:52 +01:00
Claude Code
ae1c41f0ae Initial commit: Gaming Hub foundation
Plugin-based Discord bot framework with web frontend:
- Core: Discord.js client, SSE broadcast, JSON persistence
- Plugin system: lifecycle hooks (init, onReady, routes, snapshot, destroy)
- Web: React 19 + Vite 6 + TypeScript, tab-based navigation
- Docker: multi-stage build (Node 24, static ffmpeg, yt-dlp)
- GitLab CI: Kaniko with LAN registry caching

Ready for plugin development.
2026-03-05 22:52:13 +01:00