diff --git a/client/src/common/styles/colors.sass b/client/src/common/styles/colors.sass
index d8fafb0..8b1f658 100644
--- a/client/src/common/styles/colors.sass
+++ b/client/src/common/styles/colors.sass
@@ -11,5 +11,6 @@ $pink: #ff6bb3
$blue: #4d9dff
$purple: #9c6bff
$cyan: #6bffea
+$orange: #ff9b6b
$yellow: #ffde6b
$mint-green: #85ffbd
\ No newline at end of file
diff --git a/client/src/components/YouTubePlayer/YouTubePlayer.jsx b/client/src/components/YouTubePlayer/YouTubePlayer.jsx
index 29749b6..33b0db2 100644
--- a/client/src/components/YouTubePlayer/YouTubePlayer.jsx
+++ b/client/src/components/YouTubePlayer/YouTubePlayer.jsx
@@ -4,17 +4,18 @@ import './styles.sass';
const YouTubePlayer = ({
videoId,
autoplay = false,
- volume = 50,
startTime = 45,
onReady = () => {},
onError = () => {},
+ onPlayerReady = null,
className = ''
}) => {
const iframeRef = useRef(null);
const playerRef = useRef(null);
const [isLoaded, setIsLoaded] = useState(false);
+ const [customStartTime] = useState(startTime);
- console.log("YouTubePlayer rendering with videoId:", videoId, "volume:", volume, "startTime:", startTime);
+ console.log("YouTubePlayer rendering with videoId:", videoId, "startTime:", startTime);
useEffect(() => {
if (!window.YT) {
@@ -58,18 +59,38 @@ const YouTubePlayer = ({
'showinfo': 0,
'rel': 0,
'autoplay': autoplay ? 1 : 0,
- 'start': startTime
+ 'start': startTime,
+ 'modestbranding': 1,
+ 'fs': 0,
},
events: {
'onReady': (event) => {
- console.log("YouTube player ready");
- event.target.setVolume(volume);
- if (autoplay) {
- event.target.seekTo(startTime);
- event.target.playVideo();
+ const player = event.target;
+ console.log("YouTube player ready event fired");
+
+ playerRef.current = player;
+
+ try {
+ const hasSeekTo = typeof player.seekTo === 'function';
+ console.log(`Player has seekTo: ${hasSeekTo}`);
+
+ // Start playback if needed
+ if (autoplay) {
+ player.seekTo(startTime || 0);
+ player.playVideo();
+ }
+
+ setIsLoaded(true);
+ onReady();
+
+ if (typeof onPlayerReady === 'function') {
+ console.log("Providing player instance to parent");
+ onPlayerReady(player);
+ }
+ } catch (err) {
+ console.error("Error in player ready handler:", err);
+ onError(err);
}
- setIsLoaded(true);
- onReady();
},
'onError': (event) => {
console.error("YouTube player error:", event);
@@ -94,21 +115,20 @@ const YouTubePlayer = ({
}
}
};
- }, [videoId, autoplay, onReady, onError, startTime]);
+ }, [videoId, autoplay, onReady, onError, startTime, customStartTime]);
useEffect(() => {
- if (playerRef.current && isLoaded) {
- console.log("Setting YouTube volume to:", volume);
+ if (playerRef.current && isLoaded && customStartTime !== startTime) {
try {
- playerRef.current.setVolume(volume);
+ playerRef.current.seekTo(customStartTime);
} catch (error) {
- console.error("Error setting volume:", error);
+ console.error("Error seeking to time:", error);
}
}
- }, [volume, isLoaded]);
+ }, [customStartTime, isLoaded]);
return (
-
+
);
diff --git a/client/src/components/YouTubePlayer/styles.sass b/client/src/components/YouTubePlayer/styles.sass
index 64781be..93218d9 100644
--- a/client/src/components/YouTubePlayer/styles.sass
+++ b/client/src/components/YouTubePlayer/styles.sass
@@ -1,19 +1,16 @@
.youtube-player-container
- position: fixed
- right: 20px
- bottom: 120px
- width: 300px
- height: 200px
- z-index: 100
+ width: 100%
+ margin: 10px 0
+ border-radius: 12px
+ overflow: hidden
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3)
+ background: #000
- &.hidden-player
- width: 1px
- height: 1px
- opacity: 0
- overflow: hidden
- position: absolute
- left: -9999px
- top: -9999px
+ &.visible-player
+ height: 200px
+ max-width: 100%
+ opacity: 1
+ z-index: 1
.youtube-embed
width: 100%
diff --git a/client/src/pages/Game/Game.jsx b/client/src/pages/Game/Game.jsx
index 897d930..af8e1cc 100644
--- a/client/src/pages/Game/Game.jsx
+++ b/client/src/pages/Game/Game.jsx
@@ -10,7 +10,9 @@ import {
faClock,
faCrown,
faPaperPlane,
- faCheckCircle
+ faCheckCircle,
+ faVolumeUp,
+ faStepForward
} from "@fortawesome/free-solid-svg-icons";
import { fetchPlaylistSongs } from "@/services/youtubeService.js";
import YouTubePlayer from "../../components/YouTubePlayer/YouTubePlayer";
@@ -45,13 +47,13 @@ export const Game = () => {
const timerIntervalRef = useRef(null);
const [allSongs, setAllSongs] = useState([]);
- const [playerVolume, setPlayerVolume] = useState(30);
const [playbackError, setPlaybackError] = useState(null);
const [songsLoading, setSongsLoading] = useState(false);
useEffect(() => {
if (!connected) return;
+
const eventHandlers = {
"roles-assigned": (roles) => {
const myRole = roles[socket?.id];
@@ -256,8 +258,24 @@ export const Game = () => {
setTimeLeft(0);
}, [send]);
- const handlePlayerReady = useCallback(() => {
+ const handlePlayerReady = useCallback((player) => {
+ console.log("Player ready handler called with:", player);
setPlaybackError(null);
+
+ if (player && typeof player.seekTo === 'function') {
+ console.log("Setting valid YouTube player instance");
+ setYtPlayer(player);
+
+ try {
+ const currentTime = player.getCurrentTime();
+ console.log(`Player initialized at ${currentTime}s`);
+ } catch (err) {
+ console.error("Error testing player methods:", err);
+ }
+ } else {
+ console.error("Invalid YouTube player instance provided:", player);
+ setPlaybackError("YouTube player initialization issue");
+ }
}, []);
const handlePlayerError = useCallback((error) => {
@@ -305,7 +323,22 @@ export const Game = () => {
von {currentSong.artist}
- Spiele diesen Song mit dem Tonregler!
+
+
+
+
+
+
+
Use the player above to listen, and the tone slider to play!
+
)}
@@ -521,40 +554,12 @@ export const Game = () => {
/>
)}
- {role === "composer" && currentSong && currentSong.youtubeId && (
-
- )}
-
{playbackError && (
{playbackError}
)}
- {phase === "composing" && role === "composer" && currentSong && (
-
-
- Music Volume:
- setPlayerVolume(parseInt(e.target.value))}
- className="volume-slider"
- />
-
-
- )}
-
{songsLoading && (
Loading songs...
diff --git a/client/src/pages/Game/styles.sass b/client/src/pages/Game/styles.sass
index 017f742..c5bba8a 100644
--- a/client/src/pages/Game/styles.sass
+++ b/client/src/pages/Game/styles.sass
@@ -264,6 +264,11 @@
.song-display
text-align: center
+ display: flex
+ flex-direction: column
+ align-items: center
+ width: 100%
+ margin-bottom: 30px
.song-card
display: flex
@@ -274,6 +279,11 @@
border-radius: 20px
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3)
backdrop-filter: blur(10px)
+ background: rgba(255, 255, 255, 0.08)
+ padding: 25px
+ border-radius: 20px
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3)
+ backdrop-filter: blur(10px)
border: 1px solid rgba(255, 255, 255, 0.2)
max-width: 500px
margin: 20px auto
@@ -304,295 +314,13 @@
font-size: 16px
color: rgba(255, 255, 255, 0.7)
-.phase-header
- display: flex
- justify-content: space-between
- align-items: center
- width: 100%
- margin-bottom: 30px
-
- h3
- font-size: 24px
- margin: 0
- color: $white
- text-shadow: 0 0 10px rgba(255, 255, 255, 0.3)
-
- .timer
- background: rgba(255, 255, 255, 0.1)
- padding: 10px 20px
- border-radius: 15px
- font-size: 18px
- display: flex
- align-items: center
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2)
- border: 1px solid rgba(255, 255, 255, 0.1)
-
- svg
- margin-right: 10px
- color: $yellow
-
-// Improved song selection grid
-.song-selection
- width: 100%
-
- .instruction
- text-align: center
- font-size: 20px
- margin-bottom: 30px
- color: $white
-
- .song-grid
- display: grid
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr))
- gap: 20px
- margin-top: 20px
-
- .song-option
- background: rgba(255, 255, 255, 0.1)
- border-radius: 15px
- overflow: hidden
- cursor: pointer
- transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)
- border: 2px solid transparent
- box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3)
- height: 100%
-
- &:hover:not(.disabled)
- transform: translateY(-8px) scale(1.03)
- box-shadow: 0 15px 30px rgba(0, 0, 0, 0.4), 0 0 25px rgba(255, 255, 255, 0.1)
- background: rgba(255, 255, 255, 0.15)
-
- &.selected
- border: 2px solid $yellow
- box-shadow: 0 0 25px rgba(255, 255, 0, 0.4), 0 10px 30px rgba(0, 0, 0, 0.4)
- background: rgba(255, 255, 255, 0.15)
-
- &.disabled
- opacity: 0.7
- cursor: default
-
- .song-image
- position: relative
-
- img
- width: 100%
- height: 150px
- object-fit: cover
-
- .selection-indicator
- position: absolute
- top: 10px
- right: 10px
- background: $yellow
- width: 30px
- height: 30px
- border-radius: 50%
- display: flex
- align-items: center
- justify-content: center
- box-shadow: 0 0 15px rgba(255, 255, 0, 0.7)
-
- .song-details
- padding: 15px
-
- .song-title
- font-weight: bold
- font-size: 16px
- color: $white
-
- .song-artist
- font-size: 14px
- opacity: 0.7
- margin-top: 5px
-
-// Results phase improvements
-.results-phase
- text-align: center
-
- h3
- font-size: 28px
- margin-bottom: 40px
-
- .round-results
- background: rgba(255, 255, 255, 0.08)
- backdrop-filter: blur(10px)
- border-radius: 25px
- padding: 30px
- box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4)
- border: 1px solid rgba(255, 255, 255, 0.15)
- max-width: 600px
- margin: 0 auto
-
- .scoreboard
+ .music-controls
margin-top: 30px
- padding-top: 20px
- border-top: 1px solid rgba(255, 255, 255, 0.1)
-
- h4
- margin-bottom: 15px
- font-size: 20px
-
- .scores
- display: grid
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr))
- gap: 10px
-
- .score-entry
- background: rgba(255, 255, 255, 0.1)
- border-radius: 10px
- padding: 10px 15px
- display: flex
- justify-content: space-between
- align-items: center
-
- &:nth-child(1)
- background: linear-gradient(135deg, rgba(255, 215, 0, 0.2), rgba(255, 215, 0, 0.1))
- box-shadow: 0 0 20px rgba(255, 215, 0, 0.3)
-
- &:nth-child(2)
- background: linear-gradient(135deg, rgba(192, 192, 192, 0.2), rgba(192, 192, 192, 0.1))
-
- &:nth-child(3)
- background: linear-gradient(135deg, rgba(205, 127, 50, 0.2), rgba(205, 127, 50, 0.1))
-
- .player-name
- display: flex
- align-items: center
-
- .host-icon
- color: $yellow
- margin-left: 5px
-
-// Responsive adjustments
-@media (max-width: 900px)
- .game-layout
- flex-direction: column
-
- .main-content
- margin-right: 0
- margin-bottom: 20px
-
- .chat-panel
- width: 100%
- max-height: 300px
-
-// Animations
-@keyframes slide-in-right
- 0%
- transform: translateX(30px)
- opacity: 0
- 100%
- transform: translateX(0)
- opacity: 1
-
-// ...existing animations...
-
- .song-display
+ width: 100%
+ max-width: 400px
display: flex
flex-direction: column
align-items: center
- justify-content: center
- width: 50%
- margin-right: 20px
- color: $white
- text-align: center
-
- h2
- font-size: 52pt
- color: $white
- margin-bottom: 25px
- position: relative
- z-index: 2
- background: linear-gradient(135deg, $pink, $blue 45%, $mint-green 65%, $yellow 85%, $pink)
- background-size: 300% 100%
- animation: title-shimmer 10s infinite alternate ease-in-out, title-float 6s infinite ease-in-out
- -webkit-background-clip: text
- background-clip: text
- -webkit-text-fill-color: transparent
- filter: drop-shadow(0 0 15px rgba(255, 255, 255, 0.7))
- letter-spacing: 0.1em
- font-weight: bold
-
- &:before
- content: "ToneGuessr"
- position: absolute
- z-index: -1
- left: 0
- top: 0
- background: none
- -webkit-text-fill-color: transparent
- filter: blur(15px) brightness(1.3)
- opacity: 0.6
- width: 100%
- height: 100%
-
- .song-card
- display: flex
- flex-direction: row
- align-items: center
- background: rgba(255, 255, 255, 0.08)
- padding: 25px
- border-radius: 20px
- box-shadow: 0 4px 30px rgba(0, 0, 0, 0.2), 0 0 20px rgba(255, 255, 255, 0.1)
- backdrop-filter: blur(10px)
- border: 1px solid rgba(255, 255, 255, 0.2)
- max-width: 500px
- transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275)
- will-change: transform, box-shadow
- transform: translateZ(0)
- backface-visibility: hidden
- animation: card-pulse 6s infinite alternate ease-in-out
-
- &:hover
- transform: translateY(-10px) scale(1.02)
- box-shadow: 0 15px 40px rgba(0, 0, 0, 0.3), 0 0 30px rgba(255, 255, 255, 0.15)
- border: 1px solid rgba(255, 255, 255, 0.4)
-
- img
- width: 120px
- height: 120px
- border-radius: 15px
- margin-right: 25px
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4)
- transition: transform 0.3s ease
- will-change: transform
- transform: translateZ(0)
- animation: album-rotate 10s infinite alternate ease-in-out
-
- &:hover
- transform: scale(1.1) rotate(5deg)
- box-shadow: 0 8px 30px rgba(0, 0, 0, 0.5), 0 0 40px rgba(102, 204, 255, 0.3)
-
- .song-info
- display: flex
- flex-direction: column
- align-items: flex-start
-
- .song-names
- font-size: 28pt
- color: $white
- margin-bottom: 10px
- text-shadow: 0 0 10px rgba(255, 255, 255, 0.5)
- animation: text-shimmer 5s infinite alternate ease-in-out
-
- .song-description
- font-size: 16pt
- color: $border
- opacity: 0.8
- position: relative
-
- &:after
- content: ""
- position: absolute
- bottom: -5px
- left: 0
- width: 0%
- height: 1px
- background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.7), transparent)
- transition: all 0.4s ease
-
- &:hover:after
- width: 100%
.chat-window
width: 50%
@@ -963,56 +691,13 @@
opacity: 1
transform: scale(1.05)
-.volume-controls
- position: fixed
- top: 70px
- right: 20px
- background: rgba(0, 0, 0, 0.5)
- padding: 10px 15px
- border-radius: 10px
- display: flex
- align-items: center
- z-index: 10
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3)
-
- span
- color: white
- margin-right: 10px
- font-size: 14px
-
- .volume-slider
- width: 100px
- height: 6px
- -webkit-appearance: none
- appearance: none
- background: rgba(255, 255, 255, 0.2)
- border-radius: 3px
- outline: none
-
- &::-webkit-slider-thumb
- -webkit-appearance: none
- appearance: none
- width: 16px
- height: 16px
- border-radius: 50%
- background: $yellow
- cursor: pointer
- box-shadow: 0 0 10px rgba(255, 255, 0, 0.5)
-
- &::-moz-range-thumb
- width: 16px
- height: 16px
- border-radius: 50%
- background: $yellow
- cursor: pointer
- box-shadow: 0 0 10px rgba(255, 255, 0, 0.5)
-
.playback-error
position: fixed
bottom: 10px
left: 50%
transform: translateX(-50%)
background: rgba(255, 0, 0, 0.7)
+ margin-left: 10px
color: white
padding: 8px 20px
border-radius: 20px
@@ -1025,4 +710,114 @@
0%, 80%
opacity: 1
100%
- opacity: 0
\ No newline at end of file
+ opacity: 0
+
+.player-container
+ position: fixed
+ left: 20px
+ bottom: 20px
+ display: flex
+ flex-direction: column
+ align-items: flex-start
+ z-index: 100
+ background: rgba(0, 0, 0, 0.5)
+ padding: 10px
+ border-radius: 10px
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3)
+ max-width: 320px
+
+ .player-controls
+ width: 100%
+ margin-top: 10px
+ position: relative
+ left: auto
+ top: auto
+ right: auto
+ background: none
+ box-shadow: none
+ padding: 0
+
+ .volume-controls
+ width: 100%
+ display: flex
+ align-items: center
+ background: none
+ padding: 5px 0
+ position: static
+ box-shadow: none
+
+ span
+ white-space: nowrap
+
+ .volume-slider
+ flex: 1
+ margin-left: 10px
+
+.composer-player
+ width: 300px
+ height: 169px
+ border-radius: 8px
+ overflow: hidden
+
+.composer-player
+ width: 300px
+ height: 169px
+ border-radius: 8px
+ overflow: hidden
+
+.position-jump-button
+ display: block
+ margin-top: 10px
+ padding: 8px 15px
+ background: linear-gradient(135deg, $yellow, $orange)
+ color: #fff
+ border: none
+ border-radius: 8px
+ font-size: 14px
+ font-weight: 600
+ cursor: pointer
+ transition: all 0.2s ease
+ width: 100%
+
+ &:hover
+ background: linear-gradient(135deg, lighten($yellow, 5%), lighten($orange, 5%))
+ transform: translateY(-2px)
+ box-shadow: 0 4px 15px rgba(255, 204, 0, 0.3)
+
+ &:active
+ transform: translateY(0)
+
+.player-container
+ width: 100%
+ margin-top: 20px
+ background: rgba(20, 20, 20, 0.5)
+ backdrop-filter: blur(10px)
+ padding: 15px
+ border-radius: 15px
+ border: 1px solid rgba(255, 255, 255, 0.1)
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3)
+
+ h4
+ color: $white
+ margin: 0 0 15px 0
+ text-align: center
+ font-size: 16px
+
+ .embedded-player
+ width: 100%
+ border-radius: 8px
+ overflow: hidden
+
+.song-player-container
+ width: 100%
+ max-width: 500px
+ margin: 20px auto
+ border-radius: 15px
+ overflow: hidden
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25)
+ background: rgba(0, 0, 0, 0.5)
+
+ .song-embedded-player
+ width: 100%
+ height: auto
+ aspect-ratio: 16 / 9
\ No newline at end of file