def set_voice_client(self, voice_client: discord.VoiceClient): """Set new voice client to the state. If the same client is provided, does nothing. If other voice client is present, it will be removed and all playing audio sources will be immediately finished first. TODO: Hey, we can change voice client, that is owned by guild/channel with a voice client key != our voice client key. Do something! Args: voice_client: Voice client to set. Raises: ValueError: If not a :class:`discord.VoiceClient` provided, or voice client is not connected. """ if not isinstance(voice_client, discord.VoiceClient): raise ValueError("Not a voice client") if voice_client == self._voice_client: return if not voice_client.is_connected(): raise ValueError("Voice client is not connected") if self._voice_client is not None: self.remove_voice_client() self._loop = voice_client.loop self._voice_client = voice_client self._voice_client_disconnect_source = voice_client.disconnect voice_client.disconnect = self._on_disconnect log.debug(f"Voice client has set (Voice client key ID #{self._key_id})")
async def play_forever(vc: discord.VoiceClient): event = asyncio.Event() while vc.is_connected(): audio = discord.FFmpegPCMAudio('resources/shame.mp3') vc.play(audio, after=lambda _: event.set()) await event.wait() event.clear()
def play_song(self, client: VoiceClient, url): if not client.is_connected(): return song_there = os.path.isfile("song.mp3") try: if song_there: os.remove("song.mp3") except PermissionError: print('error') return print("Someone wants to play music let me get that ready for them...") ydl_opts = { 'format': 'bestaudio/best', 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '192', }], } with youtube_dl.YoutubeDL(ydl_opts) as ydl: ydl.download([url]) for file in os.listdir("./"): if file.endswith(".mp3"): os.rename(file, 'song.mp3') def after_playing(err): song = self.get_next_song() print(song, self.playlist) self.play_song(client, song) client.play(discord.FFmpegPCMAudio("song.mp3"), after=after_playing) client.volume = 100
def callback(self, client: discord.VoiceClient, queue: collections.deque) -> None: """Handle callbacks after the voice client finishes playing.""" if not client.is_connected(): queue.clear() elif queue: now = queue.popleft() client.play(now, after=lambda e: self.callback(client, queue))
async def next_track(self, voice_client: VoiceClient): """Play next track""" if voice_client is None: self.cleanup() return if not voice_client.is_connected(): self.cleanup() return if voice_client.is_connected() and self.queue: source = self.queue.pop(0) await source.load_source() voice_client.play(source, after=lambda error: self.on_track_end( source, error, voice_client)) self.on_track_start(audio_source=source)
async def play(self, voice_channel:discord.VoiceClient, channel:discord.VoiceChannel): while len(self.playlist[channel.id]) != 0: file = self.playlist[channel.id][0] if not voice_channel.is_connected(): await voice_channel.connect(reconnect=True) self.playing[channel.id] = voice_channel voice_channel.play(discord.FFmpegPCMAudio(executable=self.ffmpeg, source=file), after=None) while voice_channel.is_playing(): if self.playing[channel.id] == None: voice_channel.stop() await voice_channel.disconnect() self.playlist[channel.id].clear() return await asyncio.sleep(0.05) if file[:3] == 'tmp': fileManager.delete_file(file) self.playlist[channel.id].pop(0) self.playing[channel.id] = None
async def _play_loop(self, voice: discord.VoiceClient) -> None: try: while voice.is_connected(): self._queue_update_flag.clear() async with self._queue_lock: if self._voice is None and len(self._queue) > 0: self._now_playing = self._queue.popleft() log.info(f"[{self._now_playing.user.name}:{self._now_playing.user.id}] " f"Now playing {self._now_playing.title}") # Launch ffmpeg process self._stream = FfmpegStream( self._now_playing, discord.opus.Encoder() ) self._voice = voice # Waits until ffmpeg has buffered audio before playing await self._stream.wait_ready(loop=self.loop) # Wait an extra second for livestreams so player clock runs behind input if self._now_playing.live is True: await asyncio.sleep(1, loop=self.loop) # Sync play start time to player start self._play_start_time = time.perf_counter() self._voice.play( # About the same as a max volume YouTube video, I think discord.PCMVolumeTransformer(self._stream, volume=0.3), after=lambda err: asyncio.run_coroutine_threadsafe( self._after_song(), loop=self.loop ) ) self._change_status(Status.PLAYING) await self._queue_update_flag.wait() except asyncio.CancelledError: pass except Exception as e: log.error(f"Unhandled exception: {e}")
async def queue_task(self, ctx: commands.Context, voice_client: discord.VoiceClient): # 왜인지는 모르겠는데 매우 불안정하네요. while voice_client.is_connected(): if ctx.guild.id not in self.queues.keys(): if voice_client.is_playing(): voice_client.stop() break try: is_loop = bool(self.queues[ctx.guild.id]["playing"]["loop"]) is_shuffle = bool( self.queues[ctx.guild.id]["playing"]["random"]) except KeyError: is_loop = False is_shuffle = False if not voice_client.is_playing() and not voice_client.is_paused( ) and (not is_loop and len(self.queues[ctx.guild.id]) <= 1): break if voice_client.is_playing() or voice_client.is_paused(): await asyncio.sleep(1) continue print(self.queues) song_id_list = [ x for x in self.queues[ctx.guild.id].keys() if x != "playing" ] next_song_id = ( sorted(song_id_list)[0] if not is_shuffle else random.choice(song_id_list)) if not is_loop else "playing" print(next_song_id) next_song = self.queues[ctx.guild.id][next_song_id].copy() embed = discord.Embed( title="유튜브 음악 재생 - 재생 시작", description= f"업로더: [`{next_song['vid_author']}`]({next_song['vid_channel_url']})\n" f"제목: [`{next_song['vid_title']}`]({next_song['vid_url']})", color=discord.Color.red(), timestamp=datetime.datetime.now( tz=datetime.timezone(datetime.timedelta(hours=9)))) embed.set_footer(text=str(next_song['req_by']), icon_url=next_song['req_by'].avatar_url) embed.set_image(url=next_song['thumb']) voice_client.play( discord.FFmpegPCMAudio(next_song["tgt_url"], before_options=get_youtube.before_args)) voice_client.source = discord.PCMVolumeTransformer( voice_client.source) voice_client.source.volume = self.queues[ ctx.guild.id]["playing"]["vol"] await ctx.send(embed=embed) if not is_loop: for k, v in next_song.items(): self.queues[ctx.guild.id]["playing"][k] = v del self.queues[ctx.guild.id][next_song_id] await asyncio.sleep(1) if ctx.guild.id in self.queues.keys(): del self.queues[ctx.guild.id] if voice_client.is_connected(): await voice_client.disconnect() await ctx.send( f"대기열이 비어있고 모든 노래를 재생했어요. `{voice_client.channel}`에서 나갈께요.")
async def play_next_queued(self, voice_client: discord.VoiceClient): if voice_client is None or not voice_client.is_connected(): return await asyncio.sleep(0.5) guild_document = await self.guild_document_from_guild( voice_client.guild) guild_queued = guild_document.get("queue", []) if len(guild_queued) == 0: # await voice_client.disconnect() return if guild_document.get("loop", False): next_song_url = guild_queued[0] else: next_song_url = guild_queued.pop(0) await self.music_db.songs.update_one({"_id": voice_client.guild.id}, {'$set': { "queue": guild_queued }}, upsert=True) local_ffmpeg_options = ffmpeg_options.copy() resume_from = 0 if type(next_song_url) == tuple or type(next_song_url) == list: next_song_url, resume_from = next_song_url local_ffmpeg_options['options'] = "-vn -ss {}".format(resume_from) volume_document = await self.bot.mongo.find_by_id( self.music_db.volumes, voice_client.guild.id) volume = volume_document.get("volume", 0.5) if next_song_url is None: self.bot.loop.create_task(self.play_next_queued(voice_client)) return next_song_url = await self.transform_single_song(next_song_url) if next_song_url is None: self.bot.loop.create_task(self.play_next_queued(voice_client)) return data = await YTDLSource.get_video_data(next_song_url, self.bot.loop) source = YTDLSource(discord.FFmpegPCMAudio(data["url"], **local_ffmpeg_options), data=data, volume=volume, resume_from=resume_from) if voice_client.guild.id in self.tts_cog.guild_queues: while len(self.tts_cog.guild_queues[voice_client.guild.id]) > 0: await asyncio.sleep(0.1) while voice_client.is_playing(): await asyncio.sleep(0.1) voice_client.play(source, after=lambda e: self.bot.loop.create_task( self.play_next_queued(voice_client))) title = await self.title_from_url(next_song_url) embed = self.bot.create_completed_embed( "Playing next song!", "Playing **[{}]({})**".format(title, next_song_url)) thumbnail_url = await self.thumbnail_from_url(next_song_url) if thumbnail_url is not None: embed.set_thumbnail(url=thumbnail_url) text_channel_id = guild_document.get("text_channel_id", None) if text_channel_id is None: print("text channel id is none") return # noinspection PyTypeChecker called_channel = self.bot.get_channel(text_channel_id) history = await called_channel.history(limit=1).flatten() print("sending message") if len(history) > 0 and history[0].author.id == self.bot.user.id: old_message = history[0] if len(old_message.embeds) > 0: if old_message.embeds[0].title == "Playing next song!": await old_message.edit(embed=embed) return await called_channel.send(embed=embed)