在Go中构建K-Pop收音机!
#开源 #go #cli #websockets

可以在此处找到完整的代码:
https://github.com/raymond-design/kpop-cli

介绍:

我们将学习如何使用 gorilla websockets websocket客户端和 faiface/beep 来流k-pop music -

设置项目:

让我们首先通过运行:
go mod init [project name]

我们需要下载两个库:

哔哔:
go get github.com/faiface/beep

Gorilla Websocket:
go get github.com/gorilla/websocket

开始编码!

如果我们首先设置项目结构会有所帮助。

首先在我们的项目目录中创建一个main.go文件。这将是我们的入口点。

然后创建更多4个目录:
connect
play
types
ui

项目结构应该看起来像这样(如果您打算推动git:

,我还建议创建一个.gitignore

The Project opened up in VsCode

用户界面

让我们首先在ui文件夹中创建一个文件。我们将文件命名为ui.go

该文件将定义一个函数,该函数打印歌曲信息的终端!首先,让我们导入"fmt"软件包:

package ui

import (
    "fmt"
)

现在,让我们创建一个名为WriteToFunction的函数。确保大写第一个字母(因为我们将在其他地方使用):

func WriteToScreen(name string, author string, album string) {
    fmt.Print("\033[H\033[2J")
    fmt.Println("Now Playing:")
    fmt.Println("Title: " + name)
    fmt.Println("Artist: " + author)
    if album != "" {
        fmt.Println("Album: " + album)
    }
}

ui.go看起来像这样:

ui.go

定义类型

GO中的一个有用模式是在一个地方定义相关的结构类型。让我们在类型目录中创建一个types.go文件。

歌曲信息将以json格式为。首先导入:

package types

import "encoding/json"

接下来,我们需要描述Webockets连接的某些类型:

type SocketRes struct {
    Op int64 `json:"op"`
    D  json.RawMessage
}

type SendData struct {
    Op int64 `json:"op"`
}

type HeartbeatData struct {
    Message   string `json:"message"`
    Heartbeat int64  `json:"heartbeat"`
}

接下来,我们将定义与歌曲本身(歌曲,专辑等)相关的一些结构:

type PlayingData struct {
    Song       Song        `json:"song"`
    Requester  interface{} `json:"requester"`
    Event      interface{} `json:"event"`
    StartTime  string      `json:"startTime"`
    LastPlayed []Song      `json:"lastPlayed"`
    Listeners  int64       `json:"listeners"`
}

type Song struct {
    ID       int64         `json:"id"`
    Title    string        `json:"title"`
    Sources  []interface{} `json:"sources"`
    Artists  []Album       `json:"artists"`
    Albums   []Album       `json:"albums"`
    Duration int64         `json:"duration"`
}

type Album struct {
    ID         int64   `json:"id"`
    Name       string  `json:"name"`
    NameRomaji *string `json:"nameRomaji"`
    Image      *string `json:"image"`
}

现在我们完成了定义,我们可以创建客户端以流音频!

创建WebSocket客户端ð

前往connect目录并创建一个connect.go文件。

在此软件包中,我们需要导入Gorilla Websocket和我们已经创建的两个软件包:

package connect

import (
    "encoding/json"
    "log"
    "time"

    "github.com/raymond-design/kpop-cli/types"
    "github.com/raymond-design/kpop-cli/ui"

    "github.com/gorilla/websocket"
)

我们还需要定义3个软件包级变量:

var conn *websocket.Conn
var done = false
var ticker *time.Ticker

让我们创建一个函数以初始化连接:

func Start(url string) { 

}

(后来,url string将是我们要从中流式传输的WebSocket服务器URL)

现在粘贴以下内容:

conn_l, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
    log.Fatal("Couldn't connect to websocket")
}
conn = conn_l

如果连接不起作用,则URL可能会出现错误!

现在,让我们运行一个匿名函数goroutine来维护WebSocket连接:

go func() {
        for {
            if done {
                conn.Close()
                break
            }
            _, msg, err := conn.ReadMessage()
            if err != nil {
                log.Fatal("Couldn't read websocket message")
            }

            handleMessage(msg)
        }
}()

我们将继续维护连接,直到程序中断或读取错误。该功能应该看起来像这样:

function code

现在我们需要实现该handleMessage函数!

func handleMessage(in []byte) {
    var msg types.SocketRes
    json.Unmarshal(in, &msg)
    switch msg.Op {
    case 0:
        var data types.HeartbeatData
        json.Unmarshal(msg.D, &data)
        setHeartbeat(data.Heartbeat)
    case 1:
        var data types.PlayingData
        json.Unmarshal(msg.D, &data)
        album := "None"
        if len(data.Song.Albums) > 0 {
            album = data.Song.Albums[0].Name
        }
        ui.WriteToScreen(data.Song.Title, data.Song.Artists[0].Name, album)
    }
}

在开始功能中,我们不断调用此功能,该功能将抓住当前的歌曲数据并打印。

为了使代码清洁器,实际设置的心跳逻辑将具有其他两个功能:

func sendHeartBeat() {
    data := types.SendData{
        Op: 9,
    }
    conn.WriteJSON(data)
}

func setHeartbeat(repeat int64) {
    sendHeartBeat()
    ticker = time.NewTicker(time.Duration(repeat) * time.Millisecond)
    go func() {
        <-ticker.C
        sendHeartBeat()
    }()
}

如果您想了解有关WebSocket Connections的更多信息,请参阅一篇有用的文章:
https://www.programmerall.com/article/821816187/

最后,我们只需要一个停止功能,该功能会突破循环:

func Stop() {
    ticker.Stop()
    done = true
}

现在我们拥有这些WebSockets连接功能,我们可以将声音带入应用程序!

包括声音!

要引入声音,我们将导入faiface/beep

package play

import (
    "log"
    "net/http"
    "time"

    "github.com/faiface/beep"
    "github.com/faiface/beep/mp3"
    "github.com/faiface/beep/speaker"
)

我们还将从这个蜂鸣器包创建一个全局var:

var stream beep.StreamSeekCloser

我们将需要两个功能。一个要玩,一个停下来。

播放功能非常简单。我们将检查HTTP URL的有效性,然后使用beep/mp3开始流式音频内容!

func Play(url string) {
    resp, err := http.Get(url)
    if err != nil {
        log.Fatal("http error")
    }

    l_streamer, format, err := mp3.Decode(resp.Body)
    stream = l_streamer
    if err != nil {
        log.Fatal("decoding error")
    }

    speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10))
    speaker.Play(stream)
}

停止功能更简单。我们只是关闭流:

func Stop() {
    stream.Close()
}

代码看起来像这样:

Play Audio Code

项目入口点

现在我们可以创建应用程序的入口点!让我们导入我们的包裹:

package main

import (
    "fmt"
    "os"
    "os/signal"

    "github.com/raymond-design/kpop-cli/connect"
    "github.com/raymond-design/kpop-cli/play"
)

现在,让我们定义我们将从:
流式传输的服务器URL

const JPOP string = "https://listen.moe/fallback"
const KPOP string = "https://listen.moe/kpop/fallback"

const JSOCKET string = "wss://listen.moe/gateway_v2"
const KSOCKET string = "wss://listen.moe/kpop/gateway_v2"

顺便说一句,您也可以现在流式j-pop音乐!

现在创建主要功能:

func main(){
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    mode := "kpop"
    var stream string
    var socket string

    if len(os.Args) == 2 {
        mode = os.Args[1]
    }
}

我们可以使用开关功能在K-Pop和J-Pop音乐之间切换:

switch mode {
    case "kpop":
        stream = KPOP
        socket = KSOCKET
    case "jpop":
        stream = JPOP
        socket = JSOCKET
    default:
        fmt.Println("Error")
        os.Exit(1)
}

现在,我们可以连接并开始流媒体音乐!

connect.Start(socket)
play.Play(stream)

interrupt := make(chan os.Signal, 1)
signal.Notify(interrupt, os.Interrupt)
<-interrupt

fmt.Println("Exiting Player")
play.Stop()
connect.Stop()

(请注意,我们停止停止解码音频,然后从WebSockets服务器断开连接)

主要功能看起来像这样:

Main Function

听电台ð¶ðÖ

  • 运行go get以获取所有依赖。
  • 在项目中运行go build .
  • 运行./kpop-cli kpop播放K-Pop Music或./kpop-cli jpop(如果您实现J-Pop)。

现在您知道如何在GO中实现声音和WebSocket流!

也尝试将来流式传输其他类型的数据ð



可以在此处找到完整的代码:
https://github.com/raymond-design/kpop-cli