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 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})")
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 play_with(self, voice_client: discord.VoiceClient): fname = self.get_filename() source = await discord.FFmpegOpusAudio.from_probe(fname) voice_client.play(source) print(f"Played {self.sound_name} ({fname}) in " f"{voice_client.channel.name} ({voice_client.guild.name})") return fname
async def _stream_media(voice_client: discord.VoiceClient, loop: asyncio.BaseEventLoop, source: YTDLSource): """Method which starts the streaming and playback of the provided URL. Args: voice_client (discord.VoiceClient): The voice client used by the bot. source (YTDLSource): Audio source which contains the audio stream. """ future = loop.create_future() if loop else asyncio.get_event_loop().create_future() voice_client.play(source, after=lambda e: future.set_result(e)) try: await asyncio.wait_for(future, timeout=None) error = future.result() if error: log.error("Player error: %s", error) except asyncio.TimeoutError: log.error("Player error: Timeout for song playback has been reached.")
async def on_voice_leave(self, self_voice_client: VoiceClient, member: Member, voice_state_before: VoiceState): # If self client voice is empty. if not self.there_is_user_in_voice(voice_state_before.channel): guild_state = self.music_manager.get_guild_state(member.guild.id) self_voice_client.pause() guild_state.waiter = asyncio.ensure_future( self.wait_for_user(self_voice_client, member.guild)) guild_state.waiting = True # print("Pause and wait for user") return return await super().on_voice_leave(self_voice_client, member, voice_state_before)
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 get_stream_player(voice_client: discord.VoiceClient): ydl.download([arguments], ) file = re.sub("[a-z]+?$", "opus", filename) def after(): asyncio.ensure_future(stream_player.notify()) return voice_client.create_ffmpeg_player(file, use_avconv=True, after=after)
def playcheck(vc: discord.VoiceClient, dummy): global isPlay global nc global mQueue if isPlay: print("start") while isPlay: if vc.is_paused() == False: time.sleep(0.5) if vc.is_playing() == False: if mQueue.popQueue() != mQueue.notPlaying: src = nc.getVideo(mQueue.nowPlaying["url"]) while os.path.getsize(src) < 128: pass vc.play(discord.FFmpegPCMAudio(src)) vc.source = discord.PCMVolumeTransformer(vc.source) vc.source.volume = 0.1 isPlay = True
def disconnector(voice_client: discord.VoiceClient, bot: commands.Bot): """ This function is passed as the after parameter of FFmpegPCMAudio() as it does not take coroutines. Args: voice_client: The voice client that will be disconnected (discord.VoiceClient) bot: The bot that will terminate the voice client, which will be this very bot """ coro = voice_client.disconnect() fut = asyncio.run_coroutine_threadsafe(coro, bot.loop) try: fut.result() except asyncio.CancelledError: pass
def play_song(self, voice_client: discord.VoiceClient, channel: discord.VoiceChannel = None) -> None: """ Plays the first song in the playlist queue. :param voice_client: The discord VoiceClient that the bot should play into. :param channel: A channel to connect to if the bot is currently not in one. :return: None """ try: print(self.playlist) source = discord.FFmpegOpusAudio( self.playlist[0], options={'use_wallclock_as_timestamps': True}) voice_client.play(source, after=self.play_after) self.playlist.pop(0) self.current_song_playing = self.playlist_data.pop(0) self.song_started_at = datetime.datetime.now() except discord.errors.ClientException as e: if 'Not connected to voice' in str(e): channel.connect()
def _send_audio_packet(self, voice_client: discord.VoiceClient, opus_packet: bytes, timestamp_frames: int) -> typing.Callable[[], None]: sock = voice_client.socket endpoint_ip = voice_client.endpoint_ip endpoint_port: int = voice_client.voice_port # type: ignore sequence = voice_client.sequence voice_client.timestamp = timestamp_frames udp_packet = getattr(voice_client, '_get_voice_packet')(opus_packet) voice_client.sequence = (voice_client.sequence + 1) & 0xffff def send() -> None: if sock is None: return try: sock.sendto(udp_packet, (endpoint_ip, endpoint_port)) except BlockingIOError: self.logger.warning( 'Network too slow, a packet is dropped. (seq={}, ts={})'. format(sequence, timestamp_frames)) return send
async def _alert(self, bot: commands.Bot, channel: discord.Channel, vc: discord.VoiceClient): ## voice alert len_pattern = len(self.pattern) to_speak = self.pattern[self.__used % len_pattern] to_speak_name = to_speak['name'] to_speak_speech = to_speak['say'] voice_file = './cache/' + self.lang + '_' + to_speak_name if not os.path.isfile(voice_file): tts = gTTS(text=to_speak_speech, lang=self.lang) tts.save(voice_file) print('Created voice') player = vc.create_ffmpeg_player(voice_file) player.start() await asyncio.sleep(5) player.stop()
def enqueue(self, url: str, voice: discord.VoiceClient): song_info = self.get_dl(url) full_song = { "url": url, "voice": voice, "title": song_info['title'], "duration": song_info.get('duration') } if self.playing: print('Song is currently playing. Adding to queue...') self.playlist.put_nowait(full_song) else: print('Queue is empty. Starting song...') self.current_song = full_song self.player = voice.create_ffmpeg_player(song_info['url'], after=self.when_finished) self.playing = True self.player.start() return full_song
def player_play(vc: discord.VoiceClient, m: str): global nc global mQueue global isPlay ret = "" if m == "#play" or m == "#p": # 再開 if vc.is_paused(): vc.resume() ret = "Resuming!" # キュー追加 elif m.find("https://www.nicovideo.jp/watch/") >= 0: ret = "add `{0}`".format(mQueue.addQueue(m.split(" ")[1])) # 新規再生 if not vc.is_playing(): if mQueue.popQueue() == mQueue.notPlaying: return src = nc.getVideo(mQueue.nowPlaying["url"]) while os.path.getsize(src) < 256: pass vc.play(discord.FFmpegPCMAudio(src)) vc.source = discord.PCMVolumeTransformer(vc.source) vc.source.volume = 0.1 if not isPlay: isPlay = True t1 = threading.Thread(target=playcheck, args=(vc, 0)) t1.start() ret = "Play {0}".format(mQueue.now()) return ret
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 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
def leaveVoice(self, _voice: discord.VoiceClient): coro = _voice.disconnect() fut = discord.compat.run_coroutine_threadsafe(coro, self.client.loop) fut.result()
async def queue_task(bot: CustomClient, ctx: commands.Context, voice: discord.VoiceClient): while True: exists = os.path.isfile(f"music/{ctx.guild.id}.json") if not exists: await ctx.send(f"`{voice.channel}`에서 나갈께요.") await voice.disconnect(force=True) break queue = await get_queue(ctx.guild.id) if voice.is_playing() or voice.is_paused(): await asyncio.sleep(1) continue elif not voice.is_playing() and len( queue.keys()) == 1 and queue["playing"]["loop"] is not True: await ctx.send(f"음악이 끝났어요. `{voice.channel}`에서 나갈께요.") await voice.disconnect(force=True) os.remove(f"music/{ctx.guild.id}.json") break vol = queue["playing"]["vol"] if queue["playing"]["loop"] is True: tgt_url = queue["playing"]["tgt_url"] voice.play( discord.FFmpegPCMAudio(tgt_url, before_options=get_youtube.before_args)) voice.source = discord.PCMVolumeTransformer(voice.source) voice.source.volume = vol await asyncio.sleep(3) continue if queue["playing"]["random"]: queue_keys = [str(x) for x in queue.keys() if not x == "playing"] tgt_name = str(random.choice(queue_keys)) tgt_queue = queue[tgt_name] else: tgt_name = list(queue.keys())[1] tgt_queue = queue[tgt_name] vid_url = tgt_queue["vid_url"] vid_title = tgt_queue["vid_title"] vid_author = tgt_queue["vid_author"] vid_channel_url = tgt_queue["vid_channel_url"] tgt_url = tgt_queue["tgt_url"] thumb = tgt_queue["thumb"] req_by = bot.get_user(int(tgt_queue["req_by"])) voice.play( discord.FFmpegPCMAudio(tgt_url, before_options=get_youtube.before_args)) voice.source = discord.PCMVolumeTransformer(voice.source) voice.source.volume = vol embed = discord.Embed(title="유튜브 음악 재생", color=discord.Colour.from_rgb(255, 0, 0)) embed.add_field( name="재생 시작", value= f"업로더: [`{vid_author}`]({vid_channel_url})\n제목: [`{vid_title}`]({vid_url})", inline=False) embed.add_field(name="요청자", value=f"{req_by.mention} (`{req_by}`)", inline=False) embed.set_image(url=str(thumb)) queue["playing"]["vid_url"] = vid_url queue["playing"]["vid_title"] = vid_title queue["playing"]["vid_author"] = vid_author queue["playing"]["vid_channel_url"] = vid_channel_url queue["playing"]["thumb"] = thumb queue["playing"]["tgt_url"] = tgt_url queue["playing"]["req_by"] = tgt_queue["req_by"] await ctx.send(embed=embed) del queue[tgt_name] await update_queue(ctx.guild.id, queue) await asyncio.sleep(3)
async def _play_stream(self, audio_source: AudioSource, voice_client: VoiceClient): voice_client.play(audio_source)
async def create_player(self, voice_client: discord.VoiceClient): await self.download() if self.location: audio_source = discord.FFmpegPCMAudio(self.location) if not voice_client.is_playing(): voice_client.play(audio_source)
async def _stop(self, context: Union[commands.Context, SlashContext], voice_client: discord.VoiceClient): voice_client.stop() await context.send('Okay, I\'ll be quiet.')
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)
def player_skip(vc: discord.VoiceClient): global nc vc.stop() nc.isDownload = False return "skip"
def player_stop(vc: discord.VoiceClient): vc.pause() return "pause"