来自软件墓地的故事:Squawk市场的出生和死亡
#saas #go #fullstack

Squawk市场服务和背景

自三月初以来可以这么说。在交易时,我遇到了许多具有基于音频新闻的服务,因此您可以在背景中保持其关注,同时专注于价格行动和市场。作为我预期会引起更多兴趣的AI Hype火车的无耻跳跃,我意识到我可以利用GPT4和各种文本到语音模型来构建类似的产品,但具有较低的延迟和较低的成本(并且更好)可靠性!)我在发布日发布了有关Squawk市场的信息-you can read that here

如果您有兴趣或只是想看看我所说的类似产品,我建议Financial Juice完全免费提供10秒的延迟提要(无隶属关系)。

最终,即使在Reddit和Twitter上做广告之后,我也没有一次试用或订阅,甚至在6月中旬写这篇文章,我也只有两个人就这项服务接近我。

但是,并非所有人都丢失了!在这篇文章中,我将进入Squawk市场的所有软件方面,以将失败的产品变成一些基于软件的阳性。

学习和外卖

走很快!

我已经知道这一点,但再次提醒 - 抓取网站并将其转换为语音,如下所述是在第二次完成的。

语音模型的文字 - 价格和质量很大不同

我最终在整个Squawk市场的创建过程中使用了三个不同的文本对语音API:

  • 我从ElevenLabs开始,在我看来,它具有令人印象深刻的声音产生,这听起来很令人印象深刻,真正的人性化和色调各不相同。他们的免费层每月最多可容纳10000个字符,只有6页(但是,如果您只需要固定数量的语音片段,这对您来说可能很棒),唯一的问题是每月订阅$ 5,我的角色限制太快了。

  • 然后我转到Google Cloud's text to speech,但我意识到Google没有免费的层(如果您与他们一起开一个新帐户,您最多可以获得300美元 - 我的免费信用已久了!)收取10美元的费用后,很快就离开了他们。

  • 我终于在Amazon's Polly上定居,他们的免费层是超级慷慨的500万个字符,或者3000页以上的文本 - 即使在Squawk Market在解析的所有内容中,我也从未到过外面

  • 的自由层

理想情况下,我希望坚持使用Elevenlabs,但没有订户(即没有收入),这在经济上没有任何意义。

这是与这三个服务中每一个接口的GO代码:

Elevenlabs:

func TextToSpeech(text string) []byte {
    log.Println("getting voice data...")

    // headers for elevenlabs
    headers := map[string]string{
    "Content-Type": "application/json",
    "xi-api-key":   os.Getenv("ELEVEN_LABS_API_KEY"),
    "Accept":       "audio/mpeg",
    }

    body := strings.NewReader(`{
    "text": "` + text + `", 
    "voice_settings": {
    "stability": 0.15, 
    "similarity_boost": 1.0
    }
    }`)

    // elli voice id MF3mGyEYCl7XYWbV9V6O
    data, err := http_helper.MakeHTTPRequest(
    "https://api.elevenlabs.io/v1/text-to-speech/MF3mGyEYCl7XYWbV9V6O",
    "POST",
    headers,
    nil,
    body,
    )
    if err != nil {
    log.Println(err)
    }

    // return the byte data generated by elevenlabs' boss bots
    return data
}

亚马逊:

// TextToSpeech converts text to speech using Amazon Polly
func TextToSpeech(text string) ([]byte, error) {
    sess := session.Must(session.NewSession(&aws.Config{
    Region: aws.String("us-east-1"),
    }))
    svc := polly.New(sess)

    input := &polly.SynthesizeSpeechInput{
    OutputFormat: aws.String("mp3"),
    Text:         aws.String("<speak><prosody rate=\"fast\">" + utils.EscapeForSSML(text) + "</prosody></speak>"),
    VoiceId:      aws.String("Amy"),
    TextType:     aws.String("ssml"),
    // british language code
    LanguageCode: aws.String("en-GB"),
    }

    output, err := svc.SynthesizeSpeech(input)
    if err != nil {
    fmt.Println(err)
    return nil, err
    }

    mp3data, err := io.ReadAll(output.AudioStream)
    if err != nil {
    fmt.Println(err)
    return nil, err
    }

    return mp3data, nil
}

Google:

func TextToSpeech(text string) []byte {
    // Instantiates a client.
    ctx := context.Background()

    client, err := texttospeech.NewClient(ctx)
    if err != nil {
    log.Fatal(err)
    }
    defer client.Close()

    // Perform the text-to-speech request on the text input with the selected
    // voice parameters and audio file type.
    req := texttospeechpb.SynthesizeSpeechRequest{
    // Set the text input to be synthesized.
    Input: &texttospeechpb.SynthesisInput{
    InputSource: &texttospeechpb.SynthesisInput_Text{Text: text},
    },

    // Build the voice request, select the language code ("en-US") and the SSML
    // voice gender ("neutral").
    Voice: &texttospeechpb.VoiceSelectionParams{
    LanguageCode: "en-gb",
    SsmlGender:   texttospeechpb.SsmlVoiceGender_FEMALE,
    // Name:         "en-US-Studio-O", // nice one but expensive
    Name: "en-GB-Neural2-A",
    },

    // Select the type of audio file you want returned.
    // TODO: could eventually be client side configurable
    AudioConfig: &texttospeechpb.AudioConfig{
    AudioEncoding: texttospeechpb.AudioEncoding_MP3,
    SpeakingRate:  1.3,
    Pitch:         0,
    },
    }

    resp, err := client.SynthesizeSpeech(ctx, &req)
    if err != nil {
    log.Fatal(err)
    }

    // The resp's AudioContent is binary.
    return resp.AudioContent
}

我们还可以实时解析任何带有语音的视频!

在建立Squawk市场时,我找到了一种将视频流(M3U8)转换为文本的方法。您将URL提供给YouTube视频,然后该视频的文字发布!它如何在引擎盖下工作?

首先,我们需要找到一个可以真正解析的M3U8文件!如果您使用YouTube视频(更喜欢实时视频),则有一个称为YT-DLP的工具,可以为我们做到这一点。我写了一个名为getTreamurlfromyoutubevideoid的函数,该功能称为YT-DLP以从YouTube视频ID中提取M3U8 URL:

func GetStreamUrlFromYoutubeVideoId(youtubeVideoId string) (string, error) {
    // use yt-dlp to get the stream url
    cmd := exec.Command("yt-dlp", "-f", "91", "-g", "https://www.youtube.com/watch?v="+youtubeVideoId)
    output, err := cmd.Output()
    if err != nil {
    return "", err
    }

    return string(output), nil
}

然后,我将其传递到函数m3U8VideostreamTofile:

func M3U8VideoStreamToFile(m3u8VideoStreamURI string, fileName string, fileType string, secondsString string) error {
    // write to flac file
    cmd := exec.Command("ffmpeg", "-y", "-i", m3u8VideoStreamURI, "-t", secondsString, "-f", fileType, "-ar", fmt.Sprint(sampleRateHertz), fileName)
    err := cmd.Start()
    if err != nil {
    fmt.Println(err)
    return err
    }
    err = cmd.Wait()
    if err != nil {
    fmt.Println(err)
    return err
    }
    return nil
}

最后,我使用打开AI的音频来将文件中的音频转换为文本:

func SpeechToText(filePath string) (*string, error) {
    c := openai.NewClient(os.Getenv("OPEN_AI_SECRET_KEY"))
    ctx := context.Background()

    req := openai.AudioRequest{
    Model:    openai.Whisper1,
    FilePath: filePath,
    }
    resp, err := c.CreateTranscription(ctx, req)
    if err != nil {
    fmt.Printf("Transcription error: %v\n", err)
    return nil, err
    }
    return &resp.Text, nil
}

总的来说,我们可以定义一个称为YoutubeVideoIdToText的函数,该函数看起来像这样:

func YoutubeVideoIdToText(youtubeVideoId string) error {
    // get m3u8 video stream url
    videoStreamUrl, err := streamfinder.GetStreamUrlFromYoutubeVideoId(youtubeVideoId)
    if err != nil {
    return err
    }

    // filename for now is more or less an intermediate, may want to change later
    fileName := "audio.mp3"

    // convert video stream to audio file
    err = videostreaming.M3U8VideoStreamToFile(videoStreamUrl, fileName, "mp3", "10")
    if err != nil {
    return err
    }

    // convert audio file to text
    // open ai cost is $0.006 per minute of audio - i.e. $0.36 per hour of audio
    text, err := open_ai.SpeechToText(fileName)
    if err != nil {
    return err
    }

    // print text to log for now
    log.Println(*text)
    return nil
}

从那里开始,它变得像您想要的一样有趣 - 您可以按原样进行文本,并继续使用gpt4之类的东西来汇总文本,或提供情感分析以沸腾转录到简单的“看涨”,“看跌”或“中性”信号。或将其链接到文本到语音模型,如上所述,然后直接阅读。

Squawk市场现在是开源的!

对于那些想学习或看到我如何将完整堆栈应用程序放在一起的人来说,Squawk市场的前端和后端存储库都是开源的!在这里检查它们:

Squawk Market Frontend

Squawk Market Backend

请注意,这篇文章不会介绍实际部署前端和后端的所有详细信息 - 如果您有兴趣自己运行实例,请查看每个人如何做到这一点或遇到麻烦!我很高兴考虑社区领导的努力,以使这些服务重新恢复并运行以供所有人共享。

谢谢!

我希望您喜欢这篇文章,并学到了一两件事,以与GO一起编写完整的堆栈应用程序!

-CHRISð»

我的任务是教授1,000,000个新兴开发人员现实世界中的软件!查看我的博客以获取更多信息:

https://chrisfrew.in/

和我的Udemy个人资料和课程:

https://www.udemy.com/user/chris-frewin/