Discord.py로 음악 봇 만들기 #3: 유튜브 음악 재생하기

안녕하세요! 김코딩입니다

지난 글에서 이벤트 처리와 유용한 명령어로 봇을 업그레이드해 봤죠? 오늘은 디스코드 서버를 더 신나게 만들어줄 음악 재생 기능을 추가해보겠습니다. 유튜브 링크를 입력하면 음성 채널에서 음악을 틀어주는 봇을 만들어볼게요! 준비물부터 코드까지 차근차근 설명해 드릴 테니 함께 시작해볼까요?


1. 지난 글 복습: 어디까지 했나요?

이전 글에서는:

  • on_member_join/remove 이벤트로 입장/퇴장 메시지 구현
  • !clear, !server 같은 명령어 추가
  • 봇 상태를 “게임 중”으로 설정

이제 이 기능 위에 음악 재생 기능을 추가해 더 멋진 봇을 만들어보겠습니다!


2. 이번 글의 목표

우리가 만들 음악 봇은 다음과 같은 기능을 갖춥니다:

  • 음성 채널 입장!join 명령어로 봇을 음성 채널에 초대
  • 유튜브 음악 재생!play <유튜브 URL>로 음악 재생
  • 음성 채널 퇴장!leave 명령어로 채널에서 나가기

3. 준비물: 추가 설치 및 설정

3-1. 필요한 패키지 설치

음악 재생을 위해 두 가지가 필요합니다:

  1. youtube_dl → 유튜브에서 오디오를 다운로드
  2. ffmpeg → 오디오를 재생 가능하게 변환

터미널에서 다음 명령어를 실행하세요:

pip install youtube_dl

그리고 ffmpeg는 별도로 설치해야 합니다:

  • Windows: FFmpeg 공식 사이트에서 다운로드 후 환경 변수에 추가
  • Mac: brew install ffmpeg (Homebrew 필요)
  • Linux: sudo apt install ffmpeg (Ubuntu 기준)

3-2. 봇 권한 확인

개발자 포털에서 봇 초대 URL 생성 시:

  • Scopes: bot 체크
  • Permissions: Connect, Speak 추가

4. 완성된 코드와 설명

import discord
from discord.ext import commands
import yt_dlp as youtube_dl
import asyncio
import os
from dotenv import load_dotenv

load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
if TOKEN is None:
TOKEN = "" # 여기에 봇 토큰 입력

bot = commands.Bot(command_prefix="!", intents=discord.Intents.all())

youtube_dl.utils.bug_reports_message = lambda: ''
ytdl_format_options = {
'format': 'bestaudio/best',
'outtmpl': '%(extractor)s-%(id)s-%(title)s.%(ext)s',
'noplaylist': True,
'nocheckcertificate': True,
'ignoreerrors': False,
'logtostderr': False,
'quiet': True,
'no_warnings': True,
'default_search': 'auto',
'source_address': '0.0.0.0',
}
ffmpeg_options = {
'options': '-vn'
}
ytdl = youtube_dl.YoutubeDL(ytdl_format_options)

class YTDLSource(discord.PCMVolumeTransformer):
def __init__(self, source, *, data, volume=0.5):
super().__init__(source, volume)
self.data = data
self.title = data.get('title')
self.url = data.get('url')

@classmethod
async def from_url(cls, url, *, loop=None):
loop = loop or asyncio.get_event_loop()
try:
data = await loop.run_in_executor(None, lambda: ytdl.extract_info(url, download=False))
if 'entries' in data:
data = data['entries'][0]
filename = data['url']
return cls(discord.FFmpegPCMAudio(filename, **ffmpeg_options), data=data)
except Exception as e:
print(f"YouTube 정보 추출 오류: {e}")
raise Exception(f"YouTube 영상을 불러올 수 없습니다. 올바른 URL인지 확인해주세요.")

@bot.event
async def on_ready():
print(f"{bot.user}가 온라인이에요!")
await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name="음악"))

@bot.command()
async def join(ctx):
if not ctx.message.author.voice:
await ctx.send("먼저 음성 채널에 들어가 주세요!")
return
channel = ctx.message.author.voice.channel
await channel.connect()
await ctx.send(f"{channel.name}에 입장했어요!")

@bot.command()
async def leave(ctx):
if ctx.voice_client:
await ctx.voice_client.disconnect()
await ctx.send("음성 채널에서 나왔어요!")
else:
await ctx.send("저는 음성 채널에 없어요!")

@bot.command()
async def play(ctx, *, url=None):
if url is None:
await ctx.send("YouTube URL을 입력해주세요! 예: `!play https://www.youtube.com/watch?v=dQw4w9WgXcQ`")
return

if not ctx.message.author.voice:
await ctx.send("먼저 음성 채널에 들어가 주세요!")
return
if not ctx.voice_client:
channel = ctx.message.author.voice.channel
await channel.connect()

async with ctx.typing():
try:
player = await YTDLSource.from_url(url, loop=bot.loop)
ctx.voice_client.play(player, after=lambda e: print(f'재생 오류: {e}') if e else None)
await ctx.send(f"지금 재생 중: {player.title}")
except Exception as e:
await ctx.send(f"오류 발생: {str(e)}")

bot.run(TOKEN)

5. 실행하기

  1. ffmpeg가 설치되었는지 확인 (ffmpeg -version 실행)
  2. 위 코드를 music_bot.py로 저장
  3. bot.run("토큰")에 여러분의 봇 토큰을 입력
  4. 실행: python music_bot.py

6. 문제 해결 팁

  • “FFmpeg not found” 오류 → ffmpeg 설치 후 시스템 PATH에 추가
  • 음성이 안 들려요 → 봇과 사용자가 같은 음성 채널에 있는지, 볼륨이 켜져 있는지 확인
  • “Forbidden” 오류 → 봇에 Connect, Speak 권한이 있는지 확인

7. 더 해볼 수 있는 기능

  • 🎵 큐 시스템 → 여러 곡을 대기열에 추가하고 순서대로 재생
  • ⏸️ 일시정지/재개!pause, !resume 명령어 추가
  • 🔍 검색 기능 → URL 대신 키워드로 검색 (예: !play despacito)

8. 마무리

이제 디스코드에서 친구들과 함께 음악을 들으며 즐겨보세요! 🎶 다음 글에서는 음악 큐 시스템이나 데이터 저장 (예: 사용자별 플레이리스트)을 다룰 예정이니 기대해주세요! 😃

One comment

Leave a Reply

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다