可以在此处找到完整的代码:
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
用户界面
让我们首先在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
看起来像这样:
定义类型
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)
}
}()
我们将继续维护连接,直到程序中断或读取错误。该功能应该看起来像这样:
现在我们需要实现该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()
}
代码看起来像这样:
项目入口点
现在我们可以创建应用程序的入口点!让我们导入我们的包裹:
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服务器断开连接)
主要功能看起来像这样:
听电台ð¶ðÖ
- 运行
go get
以获取所有依赖。 - 在项目中运行
go build .
。 - 运行
./kpop-cli kpop
播放K-Pop Music或./kpop-cli jpop
(如果您实现J-Pop)。
现在您知道如何在GO中实现声音和WebSocket流!
也尝试将来流式传输其他类型的数据ð
可以在此处找到完整的代码:
https://github.com/raymond-design/kpop-cli