首页 > 解决方案 > Discord.py 在不保存音频文件的情况下播放 Gtts

问题描述

我有这段代码可以将文本转换为音频并将其保存为 mp3,然后加入语音通道并播放音频文件

@commands.command()
async def tts(self, ctx, *, text):
     sound = gtts(text=text, lang="en", slow=False).save("test.mp3")
     channel=voice_channel.name
     await ctx.send('User is in channel: '+ channel)
     vc = await voice_channel.connect()
     vc.play(discord.FFmpegPCMAudio('test.mp3'), after=print("Done"))

如果不将文本保存为语音并直接从 gtts 播放,我将如何做到这一点

标签: pythondiscord.py

解决方案


gTTS 文档中,您可以找到直接播放声音的说明。在discord.py API 文档中,您会看到pipe可以True为该FFmpegPCMAudio方法设置一个参数。

但是,该FFmpegPCMAudio方法似乎无法正常工作,@Armster15 为此提供了一个针对 discord.py 问题的有效解决方案。您可以将他的代码复制到一个文件中(例如FFmpegPCMAudioGTTS.py)并将类名重命名为FFmpegPCMAudioGTTS以清楚起见,因为此方法不适用于普通流(仅适用于 gTTS)。

因此,您的代码可以变成:

from FFmpegPCMAudioGTTS import FFmpegPCMAudioGTTS

@commands.command()
async def tts(self, ctx, *, text):
    sound = gtts(text=text, lang="en", slow=False)
    sound_fp = BytesIO()
    sound.write_to_fp(sound_fp)
    channel = voice_channel.name
    await ctx.send('User is in channel: ' + channel)
    vc = await voice_channel.connect()
    vc.play(FFmpegPCMAudioGTTS(sound_fp.read(), pipe=True), after=print("Done"))

FFmpegPCMAudioGTTS.py

import subprocess
import shlex
import io
from discord.opus import Encoder
import discord

class FFmpegPCMAudioGTTS(discord.AudioSource):
    def __init__(self, source, *, executable='ffmpeg', pipe=False, stderr=None, before_options=None, options=None):
        stdin = None if not pipe else source
        args = [executable]
        if isinstance(before_options, str):
            args.extend(shlex.split(before_options))
        args.append('-i')
        args.append('-' if pipe else source)
        args.extend(('-f', 's16le', '-ar', '48000', '-ac', '2', '-loglevel', 'warning'))
        if isinstance(options, str):
            args.extend(shlex.split(options))
        args.append('pipe:1')
        self._process = None
        try:
            self._process = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=stderr)
            self._stdout = io.BytesIO(
                self._process.communicate(input=stdin)[0]
            )
        except FileNotFoundError:
            raise discord.ClientException(executable + ' was not found.') from None
        except subprocess.SubprocessError as exc:
            raise discord.ClientException('Popen failed: {0.__class__.__name__}: {0}'.format(exc)) from exc
    def read(self):
        ret = self._stdout.read(Encoder.FRAME_SIZE)
        if len(ret) != Encoder.FRAME_SIZE:
            return b''
        return ret
    def cleanup(self):
        proc = self._process
        if proc is None:
            return
        proc.kill()
        if proc.poll() is None:
            proc.communicate()

        self._process = None

推荐阅读