Fix youtube embed

This commit is contained in:
Mathias Wagner 2025-03-01 14:32:52 +01:00
parent a5579cc474
commit 6750b00f46
5 changed files with 211 additions and 393 deletions

View File

@ -11,5 +11,6 @@ $pink: #ff6bb3
$blue: #4d9dff
$purple: #9c6bff
$cyan: #6bffea
$orange: #ff9b6b
$yellow: #ffde6b
$mint-green: #85ffbd

View File

@ -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 (
<div className={`youtube-player-container ${className}`}>
<div className={`youtube-player-container visible-player ${className}`}>
<div ref={iframeRef} className="youtube-embed" />
</div>
);

View File

@ -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%

View File

@ -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 = () => {
<div className="song-description">von {currentSong.artist}</div>
</div>
</div>
<p className="instruction">Spiele diesen Song mit dem Tonregler!</p>
<div className="song-player-container">
<YouTubePlayer
videoId={currentSong.youtubeId}
autoplay={true}
startTime={currentSong.refrainTime || 45}
onReady={handlePlayerReady}
onPlayerReady={setYtPlayer}
onError={handlePlayerError}
className="song-embedded-player"
/>
</div>
<div className="music-controls">
<p className="instruction">Use the player above to listen, and the tone slider to play!</p>
</div>
</div>
)}
@ -521,40 +554,12 @@ export const Game = () => {
/>
)}
{role === "composer" && currentSong && currentSong.youtubeId && (
<YouTubePlayer
videoId={currentSong.youtubeId}
autoplay={phase === "composing"}
volume={playerVolume}
startTime={currentSong.refrainTime || 45}
onReady={handlePlayerReady}
onError={handlePlayerError}
className={phase === "composing" ? "" : "hidden-player"}
/>
)}
{playbackError && (
<div className="playback-error">
{playbackError}
</div>
)}
{phase === "composing" && role === "composer" && currentSong && (
<div className="player-controls">
<div className="volume-controls">
<span>Music Volume:</span>
<input
type="range"
min="0"
max="100"
value={playerVolume}
onChange={(e) => setPlayerVolume(parseInt(e.target.value))}
className="volume-slider"
/>
</div>
</div>
)}
{songsLoading && (
<div className="songs-loading-indicator">
Loading songs...

View File

@ -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
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