diff --git a/client/src/components/SongSubmissionScreen.jsx b/client/src/components/SongSubmissionScreen.jsx index e9c36f9..08c13db 100644 --- a/client/src/components/SongSubmissionScreen.jsx +++ b/client/src/components/SongSubmissionScreen.jsx @@ -21,7 +21,7 @@ const SongSubmissionScreen = () => { useEffect(() => { if (lobby) { - // Find current player's songs + // Finde den aktuellen Spieler und seine Lieder const player = lobby.players.find(p => p.id === currentPlayer.id); if (player) { setIsReady(player.isReady); @@ -29,46 +29,46 @@ const SongSubmissionScreen = () => { } }, [lobby, currentPlayer]); - // Get player's songs from the server + // Hole die Lieder des Spielers vom Server useEffect(() => { const fetchPlayerSongs = async () => { if (lobby && currentPlayer) { - console.log('Fetching songs for player:', currentPlayer); - console.log('All players in lobby:', lobby.players.map(p => ({ id: p.id, name: p.name }))); + console.log('Lieder für Spieler abrufen:', currentPlayer); + console.log('Alle Spieler in der Lobby:', lobby.players.map(p => ({ id: p.id, name: p.name }))); - // Find the current player by their ID, name, or socket ID + // Finde den aktuellen Spieler anhand seiner ID, Name oder Socket-ID let player = lobby.players.find(p => p.id === currentPlayer.id); - // If not found by ID, try by name as fallback + // Falls nicht gefunden, versuche es mit dem Namen als Fallback if (!player) { player = lobby.players.find(p => p.name === currentPlayer.name); - console.log('Player not found by ID, trying by name. Found:', player); + console.log('Spieler nicht über ID gefunden, versuche es mit Namen. Gefunden:', player); } - // If player found and has songs, update the state + // Wenn Spieler gefunden und Lieder vorhanden, aktualisiere den Zustand if (player) { - console.log('Found player:', player); + console.log('Spieler gefunden:', player); if (player.songs && Array.isArray(player.songs)) { - console.log('Found player songs for', player.name, ':', player.songs.length); - console.log('Songs data:', player.songs); + console.log('Spieler-Lieder gefunden für', player.name, ':', player.songs.length); + console.log('Lieder-Daten:', player.songs); setSongs(player.songs || []); } else { - console.log('No songs array for player:', player); + console.log('Kein Lieder-Array für Spieler:', player); setSongs([]); } } else { - console.error('Player not found in lobby! Current player:', currentPlayer.id); - console.log('Available players:', lobby.players.map(p => p.id)); + console.error('Spieler nicht in Lobby gefunden! Aktueller Spieler:', currentPlayer.id); + console.log('Verfügbare Spieler:', lobby.players.map(p => p.id)); setSongs([]); } } }; fetchPlayerSongs(); - // Important: This effect should run whenever the lobby state changes + // Wichtig: Dieser Effekt sollte ausgeführt werden, wenn sich der Lobby-Zustand ändert }, [lobby, currentPlayer]); - // Extract video ID from YouTube URL + // Extrahiere Video-ID aus YouTube-URL const extractVideoId = (url) => { if (!url) return null; @@ -88,7 +88,7 @@ const SongSubmissionScreen = () => { return null; }; - // Debounced search function + // Verzögerte Suchfunktion const handleSearch = async (query) => { if (!query.trim()) { setSearchResults([]); @@ -100,7 +100,7 @@ const SongSubmissionScreen = () => { const results = await searchYouTube(query); setSearchResults(results || []); } catch (error) { - console.error('Search failed:', error); + console.error('Suche fehlgeschlagen:', error); } finally { setIsSearching(false); } @@ -112,45 +112,45 @@ const SongSubmissionScreen = () => { if (name === 'searchQuery') { setSearchQuery(value); - // Check if the input might be a YouTube link + // Prüfe, ob die Eingabe ein YouTube-Link sein könnte const videoId = extractVideoId(value); if (videoId) { setSongForm({ youtubeLink: value }); - // Set basic information immediately for a responsive UI + // Setze grundlegende Informationen sofort für eine reaktionsschnelle Benutzeroberfläche const basicVideoInfo = { id: videoId, url: value, thumbnail: `https://img.youtube.com/vi/${videoId}/mqdefault.jpg` }; setSelectedVideo(basicVideoInfo); - setSearchResults([]); // Clear any existing search results + setSearchResults([]); // Lösche bestehende Suchergebnisse - // Fetch additional metadata from the server + // Hole zusätzliche Metadaten vom Server try { setIsLoadingMetadata(true); const metadata = await getYouTubeMetadata(videoId); if (metadata) { - // Update selected video with full metadata + // Aktualisiere ausgewähltes Video mit vollständigen Metadaten setSelectedVideo({ ...basicVideoInfo, - title: metadata.title || 'Unknown Title', - artist: metadata.artist || 'Unknown Artist', + title: metadata.title || 'Unbekannter Titel', + artist: metadata.artist || 'Unbekannter Künstler', thumbnail: metadata.thumbnail || basicVideoInfo.thumbnail }); } } catch (error) { - console.error('Error fetching video metadata:', error); + console.error('Fehler beim Abrufen der Video-Metadaten:', error); } finally { setIsLoadingMetadata(false); } } else if (value.trim()) { - // Clear any previous timeout + // Lösche vorherigen Timeout if (searchTimeout.current) { clearTimeout(searchTimeout.current); } - // Set a new timeout to prevent too many API calls + // Setze einen neuen Timeout, um zu viele API-Aufrufe zu vermeiden searchTimeout.current = setTimeout(() => handleSearch(value), 500); } else { setSearchResults([]); @@ -159,7 +159,7 @@ const SongSubmissionScreen = () => { } }; - // Function to toggle video preview + // Funktion zum Umschalten der Videovorschau const togglePreview = (result) => { if (selectedVideo && selectedVideo.id === result.id) { setPreviewVisible(!previewVisible); @@ -170,95 +170,95 @@ const SongSubmissionScreen = () => { }; const handleSelectSearchResult = (result) => { - // Make sure we have a complete result with all required fields + // Stelle sicher, dass wir ein vollständiges Ergebnis mit allen erforderlichen Feldern haben const completeResult = { id: result.id, url: result.url || `https://www.youtube.com/watch?v=${result.id}`, - title: result.title || 'Unknown Title', - artist: result.artist || 'Unknown Artist', + title: result.title || 'Unbekannter Titel', + artist: result.artist || 'Unbekannter Künstler', thumbnail: result.thumbnail || `https://img.youtube.com/vi/${result.id}/mqdefault.jpg` }; - // When a search result is selected, store the YouTube URL + // Wenn ein Suchergebnis ausgewählt wird, speichere die YouTube-URL setSongForm({ youtubeLink: completeResult.url }); - // Store the selected video with all necessary data for submission + // Speichere das ausgewählte Video mit allen notwendigen Daten zur Übermittlung setSelectedVideo(completeResult); - // Keep the search results visible but update the query field + // Behalte die Suchergebnisse sichtbar, aber aktualisiere das Abfragefeld setSearchQuery(completeResult.title); }; const handleAddSong = (e) => { e.preventDefault(); - // Use selected video data if available, otherwise fallback to search query or direct input + // Verwende die Daten des ausgewählten Videos, wenn verfügbar, sonst Fallback auf Suchanfrage oder direkte Eingabe let songData; - // Generate a consistent ID format that will work for deletion later + // Erzeuge ein konsistentes ID-Format, das später für die Löschung funktioniert const generateSongId = () => `song_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; if (selectedVideo) { - // We have a selected video with full details - use all available metadata + // Wir haben ein ausgewähltes Video mit vollständigen Details - verwende alle verfügbaren Metadaten songData = { youtubeLink: selectedVideo.url, title: selectedVideo.title, artist: selectedVideo.artist, thumbnail: selectedVideo.thumbnail, - id: generateSongId() // Consistent ID format + id: generateSongId() // Konsistentes ID-Format }; - console.log("Adding song with full metadata:", songData); + console.log("Füge Lied mit vollständigen Metadaten hinzu:", songData); } else { - // Extract YouTube URL from search query or direct input + // Extrahiere YouTube-URL aus Suchanfrage oder direkter Eingabe const youtubeLink = searchQuery.trim() || songForm.youtubeLink.trim(); if (!youtubeLink) return; - // Extract video ID to check if it's a valid YouTube link + // Extrahiere Video-ID, um zu überprüfen, ob es sich um einen gültigen YouTube-Link handelt const videoId = extractVideoId(youtubeLink); if (videoId) { - // It's a YouTube link, send it to the server for metadata resolution + // Es ist ein YouTube-Link, sende ihn zur Metadaten-Auflösung an den Server songData = { youtubeLink: youtubeLink, - // Include the videoId to make server-side processing easier + // Füge die videoId hinzu, um die serverseitige Verarbeitung zu erleichtern videoId: videoId, - id: generateSongId() // Consistent ID format + id: generateSongId() // Konsistentes ID-Format }; - console.log("Adding song with YouTube link:", songData); + console.log("Füge Lied mit YouTube-Link hinzu:", songData); } else { - // Not a YouTube link - treat as a search query - alert("Please enter a valid YouTube link or select a search result"); + // Kein YouTube-Link - behandle als Suchanfrage + alert("Bitte gib einen gültigen YouTube-Link ein oder wähle ein Suchergebnis aus"); return; } } - // Add the song and update UI when the server responds - // We use the Promise-based approach to wait for the server-generated ID for proper deletion + // Füge das Lied hinzu und aktualisiere die UI, wenn der Server antwortet + // Wir verwenden den Promise-basierten Ansatz, um auf die vom Server generierte ID für die ordnungsgemäße Löschung zu warten - // Add the song with callback to update songs when the server responds + // Füge das Lied mit Callback hinzu, um Lieder zu aktualisieren, wenn der Server antwortet addSong(songData) .then((updatedLobby) => { - console.log('Song added successfully, received updated lobby:', updatedLobby); + console.log('Lied erfolgreich hinzugefügt, aktualisierte Lobby erhalten:', updatedLobby); if (updatedLobby && currentPlayer) { const player = updatedLobby.players.find(p => p.id === currentPlayer.id); if (player && player.songs) { - console.log('Setting songs from addSong response:', player.songs); - setSongs([...player.songs]); // Create a new array to ensure state update + console.log('Setze Lieder aus addSong-Antwort:', player.songs); + setSongs([...player.songs]); // Erstelle ein neues Array, um die Zustandsaktualisierung sicherzustellen } else { - console.warn('Player or songs not found in updated lobby'); + console.warn('Spieler oder Lieder nicht in aktualisierter Lobby gefunden'); } } else { - console.warn('Missing lobby or currentPlayer in response'); + console.warn('Fehlende Lobby oder currentPlayer in der Antwort'); } }) .catch(error => { - console.error('Error adding song:', error); + console.error('Fehler beim Hinzufügen des Liedes:', error); }); - // Reset form + // Formular zurücksetzen setSongForm({ youtubeLink: '' }); setSearchQuery(''); setSearchResults([]); @@ -277,10 +277,10 @@ const SongSubmissionScreen = () => { } }; - // Check if we've submitted enough songs + // Prüfe, ob wir genügend Lieder eingereicht haben const canSubmitMoreSongs = lobby && songs.length < lobby.settings.songsPerPlayer; - // Extract YouTube video ID from various YouTube URL formats + // Extrahiere YouTube-Video-ID aus verschiedenen YouTube-URL-Formaten const getYoutubeVideoId = (url) => { if (!url) return null; @@ -299,7 +299,7 @@ const SongSubmissionScreen = () => { return null; }; - // Get YouTube thumbnail URL from video URL + // Hole YouTube-Thumbnail-URL aus Video-URL const getYoutubeThumbnail = (url) => { const videoId = getYoutubeVideoId(url); return videoId ? `https://img.youtube.com/vi/${videoId}/mqdefault.jpg` : null; @@ -323,7 +323,7 @@ const SongSubmissionScreen = () => { style={{ width: `${(songs.length / (lobby?.settings?.songsPerPlayer || 1)) * 100}%` }} > -

{songs.length} / {lobby?.settings?.songsPerPlayer || 0} songs

+

{songs.length} / {lobby?.settings?.songsPerPlayer || 0} Lieder

@@ -394,7 +394,7 @@ const SongSubmissionScreen = () => { )}
- {/* Show selected video without embedded player */} + {/* Zeige ausgewähltes Video ohne eingebetteten Player */} {selectedVideo && (
@@ -421,8 +421,8 @@ const SongSubmissionScreen = () => {
-

{selectedVideo.title || 'Unknown Title'}

-

{selectedVideo.artist || 'Unknown Artist'}

+

{selectedVideo.title || 'Unbekannter Titel'}

+

{selectedVideo.artist || 'Unbekannter Künstler'}

@@ -450,8 +450,8 @@ const SongSubmissionScreen = () => {
-

{result.title || 'Unknown Title'}

-

{result.artist || 'Unknown Artist'}

+

{result.title || 'Unbekannter Titel'}

+

{result.artist || 'Unbekannter Künstler'}

))} diff --git a/client/src/components/VotingScreen.jsx b/client/src/components/VotingScreen.jsx index 5e9f913..92cae9d 100644 --- a/client/src/components/VotingScreen.jsx +++ b/client/src/components/VotingScreen.jsx @@ -11,34 +11,34 @@ function VotingScreen() { const [selectedSong, setSelectedSong] = useState(null); const [countdown, setCountdown] = useState(null); - // Get current battle + // Hole aktuellen Kampf const battle = lobby?.currentBattle || null; - // Calculate the tournament phase based on the round number and total songs + // Berechne die Turnierphase basierend auf der Rundennummer und Gesamtliedern const tournamentPhase = useMemo(() => { if (!lobby || !battle) return ''; - // Get total number of songs in the tournament + // Hole Gesamtanzahl der Lieder im Turnier const totalSongs = lobby.songs?.length || 0; - if (totalSongs === 0) return 'Preliminaries'; + if (totalSongs === 0) return 'Vorrunden'; - // Calculate total rounds needed for the tournament + // Berechne die insgesamt benötigten Runden für das Turnier const totalRounds = Math.ceil(Math.log2(totalSongs)); const currentRound = battle.round + 1; const roundsRemaining = totalRounds - currentRound; - if (roundsRemaining === 0) return 'Finals'; - if (roundsRemaining === 1) return 'Semi-Finals'; - if (roundsRemaining === 2) return 'Quarter-Finals'; - return 'Contestant Round'; + if (roundsRemaining === 0) return 'Finale'; + if (roundsRemaining === 1) return 'Halbfinale'; + if (roundsRemaining === 2) return 'Viertelfinale'; + return 'Vorrunde'; }, [lobby, battle]); - // Check if player has already voted + // Prüfe, ob der Spieler bereits abgestimmt hat useEffect(() => { if (battle && battle.votes && currentPlayer) { - // Check if player's ID exists in votes object - // Since votes is sent as an object, not a Map + // Prüfe, ob die ID des Spielers im Stimmen-Objekt existiert + // Da Stimmen als Objekt, nicht als Map gesendet werden const votesObj = battle.votes || {}; setHasVoted(Object.prototype.hasOwnProperty.call(votesObj, currentPlayer.id) || Object.keys(votesObj).includes(currentPlayer.id)); @@ -47,22 +47,22 @@ function VotingScreen() { } }, [battle, currentPlayer]); - // Handle vote selection + // Behandle Stimmenwahl const handleVoteSelect = (songId) => { if (hasVoted) return; setSelectedSong(songId); }; - // Submit final vote + // Sende endgültige Stimme const handleSubmitVote = async () => { if (!selectedSong || hasVoted) return; await submitVote(selectedSong); - // Setting hasVoted is now handled by the useEffect that checks votes + // setHasVoted wird jetzt durch den useEffect behandelt, der die Stimmen prüft }; - // Get YouTube video IDs from links + // Hole YouTube-Video-IDs aus Links const getYouTubeId = (url) => { if (!url) return null; @@ -75,12 +75,12 @@ function VotingScreen() { if (!battle || !battle.song1) { return (
-

Preparing the next battle...

+

Bereite den nächsten Kampf vor...

); } - // Handle "bye" rounds where a song advances automatically + // Behandle "Freilos"-Runden, in denen ein Lied automatisch weiterkommt if (battle.bye === true && battle.song1 && !battle.song2) { const song1Id = getYouTubeId(battle.song1?.youtubeLink || ''); @@ -88,10 +88,10 @@ function VotingScreen() {

- Automatic Advance + Automatisches Weiterkommen

- Round {battle.round + 1} + Runde {battle.round + 1}
@@ -102,7 +102,7 @@ function VotingScreen() {

{battle.song1.title}

{battle.song1.artist}

-

This song automatically advances to the next round!

+

Dieses Lied kommt automatisch in die nächste Runde!

@@ -113,7 +113,7 @@ function VotingScreen() { ) : (
- No video available + Kein Video verfügbar
)} @@ -121,7 +121,7 @@ function VotingScreen() {
+ Abstimmen +
)}
-

{hasVoted ? 'Waiting for other players to vote...' : 'Choose your favorite!'}

+

{hasVoted ? 'Warte auf andere Spieler...' : 'Wähle deinen Favoriten!'}

- {battle.voteCount || 0} of {lobby?.players?.filter(p => p.isConnected).length || 0} votes + {battle.voteCount || 0} von {lobby?.players?.filter(p => p.isConnected).length || 0} Stimmen
- {/* Player voting status list */} + {/* Liste der Spielerstimmen */}
-

Voters

+

Abstimmende