3

我正在使用 Python 中的 flask-ask 框架来开发一个 Alexa-Skill,它只播放 YouTube 视频的音频流而不下载它。

因此我用pafy

以下函数从 YouTube 视频中获取音频 URL:

def get_audio():
    video = pafy.new("https://www.youtube.com/watch?v=ALdKl2HRSoI")
    bestaudio = video.getbestaudio(preftype="m4a")
    playurl = bestaudio.url
    return playurl

此功能应该播放音频

@ask.intent('StoryIntent')
def story():
    speech = "I am starting to play the story"
    stream_url = get_audio("https://www.youtube.com/watch?v=ALdKl2HRSoI")
    return audio(speech).play(stream_url)

不幸的是,它不适用于我从 pafy 获得的网址。Alexa什么也没玩。

我打印了从 pafy 获得的 url,它看起来像这样:

Pafy:Youtube 音频 URL

如果我使用以下 URL stream_url,一切正常:

S3 上的示例音频文件

我还尝试了什么: 我从“Pafy:Youtube-Audio URL”下载了音频文件并将其上传到 S3,并使用了 S3 中的链接,一切正常。但我不想为我的方法下载 YouTube 视频。

4

1 回答 1

2

我知道这个问题已经很老了,但是在过去的几天里我尝试了类似的东西,实际上我很沮丧,因为我花了很长时间才找到解决方案。所以也许我可以省去一些麻烦。

与 TO 类似,我尝试创建一个 Alexa Skill 来播放 Youtube 音频。这可以用 pafy 来完成,正如问题中所指出的那样,使用bestaudio = video.getbestaudio(). 由于我们不打算下载音频,因此无需指定格式。生成的链接将是 .webm 格式,但只有音频部分,可以通过在浏览器中打开链接进行测试。

这是此部分的代码示例:

import pafy

def get_audio(url):
    video = pafy.new(url)
    best = video.getbestaudio()
    return best.url

yt_url = "https://www.youtube.com/watch?v=iu7289s7l64"
audio_url = get_audio(yt_url)
print(audio_url)

这实际上对我来说在本地运行良好。但是,在将代码部署到 AWS Lambda 时,它不起作用。Alexa 会报告请求的技能响应存在问题,而 CloudWatch 没有显示任何错误或日志输出。

这里有两件事需要注意:

  1. 确保库已打包并上传。但是,这将在 CloudWatch Logs 中引发错误,并且在使用新的 ASK CLI 2.x 并在 require.txt 中添加 pafy 和 youtube-dl 时不会出现问题。当使用旧版本的 ASK CLI (1.x) 时,我遇到了问题,因为 requirements.txt 被忽略了。
  2. 在 AWS 控制台中增加 Lambda 函数的超时。默认情况下,它设置为 3s,这在这种情况下是短路的。长视频的调用似乎最多需要 10 秒,为了安全起见,我将其设置为 30 秒。由于某种原因,这比本地呼叫所需的时间长得多(如果有人对此有见解,我会很好奇)。然而,这就是让我陷入困境的原因。

由于 TO 还询问如何调用音频流,这是我的解决方案:

class StoryIntent(AbstractRequestHandler):

    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        return ask_utils.is_intent_name("StartPlayingIntent")(handler_input)

    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        yt_url = "https://www.youtube.com/watch?v=iu7289s7l64"
        url = get_audio(yt_url)
        return handler_input.response_builder.speak('I am starting to play the story').add_directive(
            PlayDirective(
                play_behavior=PlayBehavior.REPLACE_ALL,
                audio_item=AudioItem(
                    stream=Stream(
                        token=url,
                        url=url,
                        expected_previous_token=None),
                    metadata=None
                )
            )
        ).set_should_end_session(True)

我不确定在回答这个问题时上面的代码是否有效。到今天,基于类的意图很常见,所以我们开始吧。

tl:dr增加 Lambda 函数的 Timeout,默认为 short。

于 2020-09-24T00:36:47.640 回答