본문 바로가기

코딩/ReactJS

리액트에서 유튜브 API를 이용해 뮤직 플레이어 만들기

반응형

제목은 뭔가 완성된 프로그램을 만든 것처럼 보이지만

내가 만든건 프로그레스바(재생바)가 동작하지 않는 반쪽짜리 프로그램이다.

UI에서부터 느껴지는 반푼이 프로그램의 기운...

주제에 반응형으로 늘어났다 줄어들었다 한다 ㅎ

 

아무튼 이걸 어떻게 만들었냐면

유튜브 ifram API라는걸 이용해서 만들었다.

 

ifram player API에 대한 자세한 정보는 아래 링크를 참조하면 된다.

https://developers.google.com/youtube/iframe_api_reference?hl=ko 

 

iframe 삽입에 대한 YouTube Player API 참조 문서  |  YouTube IFrame Player API  |  Google for Developers

애플리케이션에 YouTube 플레이어를 삽입합니다.

developers.google.com

그런데 위 레퍼런스대로 영상을 땡겨오면

내가 만든 것 같은 커스텀 미디어 컨트롤러 UI가 적용되지 않은

유튜브 영상 그대로가 퍼와진다.

 

나도 처음엔 당황했었는데 단순히 크기를 0으로 수정하면 영상이 사라지고

커스텀 컨트롤러 UI를 적용할 수 있다.

 

아래 코드는 내 코드에서 axios 통신하는 부분과 앨범 커버 부분만 제외한 코드인데

axios 통신을 통해 서버에서 앨범 커버 이미지의 URL을 불러오기 때문에

아래 코드를 그대로 가져다 쓸 경우 오류를 발생할 수 밖에 없으므로 해당 부분은 제외했다.

 

참고로 MUI를 이용해서 코드를 작성했기 때문에

MUI가 설치되어있지 않다면

npm update를 이용해 패키지를 한 번 업데이트 하고 코드를 실행해야 한다.

import React,{useEffect, useState} from "react";
import { useTheme } from "@mui/material/styles";
import {
  Box,
  Card,
  CardContent,
  CardMedia,
  IconButton,
} from "@mui/material";
import SkipPreviousIcon from "@mui/icons-material/SkipPrevious";
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import PauseIcon from '@mui/icons-material/Pause';
import SkipNextIcon from "@mui/icons-material/SkipNext";

export default function Musiccontroller() {
  useEffect(() => {
    const tag = document.createElement("script");
    tag.src = "https://www.youtube.com/iframe_api";
    const firstScriptTag = document.getElementsByTagName("script")[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

    window.onYouTubeIframeAPIReady = initializePlayer;

    return () => {
      delete window.onYouTubeIframeAPIReady;
    };
  },[]);

  const theme = useTheme();

  const [player, setPlayer] = useState(null);
  const [videoTitle, setVideoTitle] = useState("");
  const [progress, setProgress] = useState(0);
  const [currentTime, setCurrentTime] = useState(0);
  const [totalTime, setTotalTime] = useState(0);
  const [isPlaying, setIsPlaying] = useState(false);
  const [volume, setVolume] = useState(50);

  const initializePlayer = () => {
    setPlayer(
      new window.YT.Player("player", {
        height: "0", // 영상은 표시하고 싶지 않을때 크기를 0으로 만들면 됨
        width: "0",
        videoId: "JGwWNGJdvx8", //music id, 원하는 영상의 musicId를 넣으면 해당 영상 재생
        playerVars: {
          rel: 0,
          controls: 0,
          autoplay: 1,
          loop: 1,
          playsinline: 1,
        },
        events: {
          onReady: onPlayerReady,
          onStateChange: onPlayerStateChange,
        },
      })
      
    );
  };

  const onPlayerReady = (event) => {
    setVideoTitle(event.target.getVideoData().title);
    setTotalTime(event.target.getDuration());
    setInterval(updateProgressBar, 1000);
  };

  const onPlayerStateChange = (event) => {
    if (event.data === window.YT.PlayerState.PLAYING) {
      setIsPlaying(true);
    } else {
      setIsPlaying(false);
    }
  };

  const updateProgressBar = () => {
    if(player){
    const currentTime = player.getCurrentTime();
    const duration = player.getDuration();
    setProgress((currentTime / duration) * 100);
    setCurrentTime(currentTime);
    }
  };

  const formatTime = (time) => {
    const minutes = Math.floor(time / 60);
    const seconds = Math.floor(time % 60);
    return `${minutes}:${seconds < 10 ? "0" : ""}${seconds}`;
  };

  const togglePlayPause = () => {
    if (isPlaying) {
      player.pauseVideo();
    } else {
      player.playVideo();
    }
  };

  const seekToTime = (value) => {
    const duration = player.getDuration();
    const seekTime = (value / 100) * duration;
    player.seekTo(seekTime);
  };

  const changeVolume = (newVolume) => {
    player.setVolume(newVolume);
    setVolume(newVolume);
  };

  return (
    <>
    <div id="player"></div>
    <Card sx={{ display: "flex", background: "transparent", color: "white" }}>
      <Box
        sx={{
          display: "flex",
          flexDirection: "column",
        }}>
        <CardContent sx={{ flex: "1 0 auto", textAlign: "center" }}>
          <div style={{maxWidth:150}}>{videoTitle}</div>
        </CardContent>
        <div style={{width: 150}}>
         <input
        type="range"
        value={progress}
        onChange={(e) => seekToTime(e.target.value)}
      />
      
      <span>{formatTime(currentTime)}</span> /
      <span>{formatTime(totalTime)}</span>
      </div>
      <div style={{width: 150}}>
      <input
        type="range"
        value={volume}
        onChange={(e) => changeVolume(e.target.value)}
        min="0"
        max="100"
      />
      </div>
        <Box
          sx={{
            display: "flex",
            alignItems: "center",
            pl: 1,
            pb: 1,
          }}>
          <IconButton
            aria-label="previous"
            sx={{
              color: "white",
            }}>
            {theme.direction === "rtl" ? (
              <SkipNextIcon />
            ) : (
              <SkipPreviousIcon />
            )}
          </IconButton>
          <IconButton aria-label="play/pause" onClick={togglePlayPause}>
            {isPlaying?<PauseIcon sx={{ height: 38, width: 38, color: "white" }} />: <PlayArrowIcon sx={{ height: 38, width: 38, color: "white" }} />}
            
          </IconButton>
          <IconButton
            aria-label="next"
            sx={{
              color: "white",
            }}>
            {theme.direction === "rtl" ? (
              <SkipPreviousIcon />
            ) : (
              <SkipNextIcon />
            )}
          </IconButton>
        </Box>
      </Box>
    </Card>
    </>
  );
}

생각보다 코드가 길다...

 

이 코드를 구현하면서 어려웠던 부분이 있었는데

사실 이 코드는 chatGPT가 만들어준 코드이다ㅎ

그러다보니 최초로 chatGPT가 만들어준 코드는

쌩 날것 그대로의, 눈뜨고 보기 어려운 수준의 UI로 이루어져 있었고

UI를 조금 손보고 싶어서 MUI를 이용해

아이콘도 끼얹고 디자인도 조금 끼얹었다.

 

근데 커스텀 컨트롤러 UI를 다 만들고 chatGPT의 코드에서

함수 부분과 useState, useEffect 부분을 모조리 다 복붙 했는데도

커스텀 컨트롤러 UI를 이용한 영상 재생이 안되어서 좀 멘붕이었다.

 

한참 삽질하다가 이유를 발견할 수 있었는데

내가 UI부분을 죄다 뜯어고치느라

chatGPT가 작성한 UI부분 코드를 제대로 가져오지 않아서

오류가 발생하는거였다.

<div id="player"></div>

내가 빼먹은 코드는 이 작고 하찮은 코드 한줄이었다.

이 짧은 코드가 iframe API로부터 불러온

동영상이 위치할 페이지 식별하는 역할을 한다.

영상을 불러와야 재생을 하든 말든 할텐데

아무런 미디어 데이터도 없는 상황에서

재생 버튼을 연타했으니 컴퓨터도 좀 당황스러웠을 것 같다.

 

발견한 방법도 좀 웃긴데

chatGPT의 코드를 한 10번 쯤 다시 봤을 때

"어 얘는 왜 div태그 안에 아무런 내용이 없지??" 싶어서

리액트 코드쪽에 복붙해봤고,

그제서야 커스텀 컨트롤러 UI가 정상적으로 동작 했다.

 

아무튼 그랬다...

반응형