파이썬 디스 코드 음악 재생 - paisseon diseu kodeu eum-ag jaesaeng

SARACEN's Blog


  • 기능(아래의 기능은 개발이 진행되면서 추가 혹은 변경, 제거가 될 수 있습니다.)
    • 유튜브 영상의 음성 출력
    • 추가한 리스트 목록 확인
    • 플레이리스트(추가한 노래 리스트) 저장 및 불러오기
    • 리스트 목록 개별 제거
    • loop 기능
    • leave 기능
    • skip 기능
    • pause 기능
    • resume 기능

일단은 되는대로 소스를 작성하고 완성 후에 변수 및 함수를 정리하도록 하겠습니다.

이번 포스팅에는 유튜브 영상의 음원을 가져와서 디스코드에서 재생을 시키는 기능을 구현하겠습니다.

시작하기 앞서 해당 개발에 뼈대가 되는 모듈은 discord.py입니다.

디스코드 봇 개발에 대한 기본적인 지식은 함양하고 있는 사람을 기반하여 작성된 글입니다.

추후에 디스코드 봇 개발의 기초적인 부분도 올리려고 하고 있습니다.(초기 설정 부분은 워낙 자료가 많아서...)


  • 간략한 구현 과정
    • youtube_search 모듈을 이용하여 특정 키워드에 대한 영상 중 최상단에 노출되는 영상의 제목과 주소를 가져옴
    • youtube_dl을 이용해서 음원을 다운로드하고, FFMPEG를 이용하여 디스코드 음성 채팅방에 출력
  • 파이썬으로 구현된 코드
    • async def song_start(voice, url):
          try:
              if not voice.is_playing() and not voice.is_paused():
                  ydl_opts = {'format': 'bestaudio'}
                  FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'}
                  with youtube_dl.YoutubeDL(ydl_opts) as ydl:
                      info = ydl.extract_info(f'https://www.youtube.com{url}', download=False) # 파일로 다운로드 하지 않고 재생
                      URL = info['formats'][0]['url']
                  
                  voice.play(discord.FFmpegPCMAudio(URL, **FFMPEG_OPTIONS))
              #voice.play(discord.FFmpegPCMAudio(executable = './ffmpeg-4.4-full_build-shared/bin/ffmpeg.exe', source='./song.mp3'))
      
              while voice.is_playing() or voice.is_paused():
                  await asyncio.sleep(0.1)
          except:
              return
      
      @bot.command(aliases = ['play', 'p', 'ㅔ'])
      async def Play(ctx,*,  keyword):
          try:
              results = YoutubeSearch(keyword, max_results=1).to_dict() # title과 url_suffix를 사용. 자세한 내용은 하단 링크 참고
      
              channel = ctx.author.voice.channel
              if bot.voice_clients == []:
                  await channel.connect()
                  #await ctx.send("connected to the voice channel, " + str(bot.voice_clients[0].channel))
              voice = bot.voice_clients[0]
              
              await song_start(voice, results[0]['url_suffix'])
          except:
              await ctx.send("Play Error")

YouTubeSearch 같은 경우에는 다음의 홈페이지를 참고하시면 더 많은 정보를 얻을 수 있습니다.

https://github.com/alexmercerind/youtube-search-python

유튜브의 파싱같은 경우는 처음에 beautifulsoup으로 진행하려 했으나 영상을 추천해주는 부분은 모두 동적인 부분이라 불가능했습니다. 이에 selenium으로 경로를 틀던 찰나에 해당 모듈(YouTubeSearch)을 발견하여 이용하게 되었습니다.

다음 시간에는 플레이리스트에 관련된 기능을 넣어보겠습니다. 감사합니다.

파이썬 디스 코드 음악 재생 - paisseon diseu kodeu eum-ag jaesaeng

드디어 방치해두었던 음악 봇을 다시 만들어보자

사실 디코에서 굳이 음악을 듣지 않아서 좀 밀렸다

저번에는 유튜브에서 youtube_dl을 이용해 파일을 다운로드 받고 ffmpeg로 mp3로 바꾼 뒤 그걸 봇이 찾아서 재생했다

lektion-von-erfolglosigkeit.tistory.com/71

디스코드 봇 만들기#5 - 음악재생

⚠ 야매로 만든 것이니 따라하지 않는 것을 추천합니다 이제 봇이 음성채널에 입장하거나 퇴장할 수 있으니 본격적으로 음악을 재생시켜보자 FFmpeg www.ffmpeg.org/ FFmpeg Converting video and audio has neve..

lektion-von-erfolglosigkeit.tistory.com

파이썬 디스 코드 음악 재생 - paisseon diseu kodeu eum-ag jaesaeng

괜히 어설프게 혼자 만들어서 저 사단이 났으니 이번엔 그냥 유튜브 영상보고 따라 만들예정이다

음악 기능은 디코 봇의 거의 기본기능이라 예제도 많다.


왜인지 유튜브 영상을 보고 따라하려다가 이해가 안되서 API reference만 주구장창 읽고 있다...

아무래도 분석하는 글을 따로 쓰고 와야할 것 같다


음성채널

자 이제 제대로 만들어보자

일단 봇이 음성채널에 들어가는 것부터 다시 해보자

@bot.command()
async def play(ctx):
    channel = ctx.author.voice.channel
    await channel.connect()

저번 글에서도 한번 했으니 크게 어렵지는 않다

다만 저번과 다르게 좀 더 깊게 이해한 것 뿐이다

다음은 음성채널에서 나가기

단순히 생각해서 channel.disconnect()를 하면 될 것 같지만

안타깝게도 disconnet 함수는 ctx.author.voice.channel에 존재하지 않는다

파이썬 디스 코드 음악 재생 - paisseon diseu kodeu eum-ag jaesaeng
파이썬 디스 코드 음악 재생 - paisseon diseu kodeu eum-ag jaesaeng

그래서 VoiceClient로 바꿔보기로 했다.

파이썬 디스 코드 음악 재생 - paisseon diseu kodeu eum-ag jaesaeng

VoiceClient는 bot.voice_clients를 통해 List 형태로 불러올 수 있다

다만 이걸로 connect는 하기 힘든게 bot.voice_clients는 봇이 음성채널에 들어가 있지 않으면 []를 반환한다

그래서 저번과 같이 connect와 disconnect 방법을 다르게 해주었다

@bot.command()
async def leave(ctx):
	await bot.voice_clients[0].disconnect()

거의 똑같이 만들었지만 확실히 이해하고 코딩을 하니 달라보이는 것 같다

음악재생

드디어 이제 노래를 재생시켜보자

디코에서 굳이 음악을 듣지 않아서 좀 밀렸다

저번에는 유튜브에서 youtube_dl을 이용해 파일을 다운로드 받고 ffmpeg로 mp3로 바꾼 뒤 그걸 봇이 찾아서 재생했다

다행이 찾아보니 다운로드 받지 않고도 바로 재생할 수 있었다

stackoverflow.com/questions/57688808/playing-music-with-a-bot-from-youtube-without-downloading-the-file

Playing music with a bot from Youtube without downloading the file

How would i go about playing music using a discord bot from Youtube without downloading the song as a file? I've already had a look at the included music bot in the discord.py documentation but th...

stackoverflow.com

파이썬 디스 코드 음악 재생 - paisseon diseu kodeu eum-ag jaesaeng

test.py라는 새로운 파일을 만들어서 실험해봤다

import discord
import youtube_dl
from discord.ext import commands

bot = commands.Bot(command_prefix = "!")

@bot.command()
async def play(ctx, url):
    channel = ctx.author.voice.channel
    if bot.voice_clients == []:
    	await channel.connect()
    	await ctx.send("connected to the voice channel, " + str(bot.voice_clients[0].channel))

    ydl_opts = {'format': 'bestaudio'}
    FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'}
    with youtube_dl.YoutubeDL(ydl_opts) as ydl:
        info = ydl.extract_info(url, download=False)
        URL = info['formats'][0]['url']
    voice = bot.voice_clients[0]
    voice.play(discord.FFmpegPCMAudio(URL, **FFMPEG_OPTIONS))

@bot.command()
async def leave(ctx):
    await bot.voice_clients[0].disconnect()


bot.run("your token here")

voice는 VoiceClient인 bot.voice_cliens[0]를 가져와서 VoiceClient.play()와 discord.FFmpegPCMAudio()를 사용해서

음악을 재생한다.

info는 url에 대한 모든 정보(제목, 업로더, 길이 등...)를 가져오고

그중 url format만 뽑아서 URL에 저장한다

그리고 URL를 discord.FFmpegPCMAudio의 source에 넣어주면 된다

더보기

위의 stackoverflow글을 보면 알겠지만 이방법이 조금 문제가 있다고 한다

그래서 **FFMPEG_OPTIONS를 추가했다고 하는데 이건 잘 모르겠다

파이썬 디스 코드 음악 재생 - paisseon diseu kodeu eum-ag jaesaeng

일단 뭐... 재생은 되니까....

그리고 VoiceClient에 있는 pause와 resume, stop도 한번 시도해보기로 했다.

@bot.command()
async def pause(ctx):
    if not bot.voice_clients[0].is_paused():
        bot.voice_clients[0].pause()
    else:
        await ctx.send("already paused")

@bot.command()
async def resume(ctx):
    if bot.voice_clients[0].is_paused():
        bot.voice_clients[0].resume()
    else:
        await ctx.send("already playing")
        
@bot.command()
async def stop(ctx):
	if bot.voice_clients[0].is_playing():
    	bot.voice_clients[0].stop()
    else:
    	await ctx.send("not playing")

확실히 저번과 다르게 조금이라도 공부하고 하니 잘되는 것 같다

이로써 미뤄두었던 음악봇을 조금이나마 만들어봤다

url을 직접 입력해야하고 재생목록도 없고 pause,resume,stop이 미흡히지만 일단 완성!

다음 글은 디스코드 봇 프로젝트 마지막글

이제 자체 서버에서 봇을 돌려보자