go-xmpp/_examples/xmpp_jukebox/xmpp_jukebox.go

150 lines
3.7 KiB
Go
Raw Normal View History

2016-03-14 03:31:31 -07:00
// Can be launched with:
// ./xmpp_jukebox -jid=test@localhost/jukebox -password=test -address=localhost:5222
package main
import (
"encoding/xml"
2016-03-14 03:31:31 -07:00
"flag"
"fmt"
"log"
"os"
"strings"
"github.com/processone/mpg123"
"github.com/processone/soundcloud"
"gosrc.io/xmpp"
"gosrc.io/xmpp/stanza"
2016-03-14 03:31:31 -07:00
)
// Get the actual song Stream URL from SoundCloud website song URL and play it with mpg123 player.
const scClientID = "dde6a0075614ac4f3bea423863076b22"
func main() {
jid := flag.String("jid", "", "jukebok XMPP Jid, resource is optional")
2016-03-14 03:31:31 -07:00
password := flag.String("password", "", "XMPP account password")
address := flag.String("address", "", "If needed, XMPP server DNSName or IP and optional port (ie myserver:5222)")
flag.Parse()
2019-06-19 02:19:49 -07:00
// 1. Create mpg player
player, err := mpg123.NewPlayer()
2016-03-14 03:31:31 -07:00
if err != nil {
log.Fatal(err)
}
2019-06-19 02:19:49 -07:00
// 2. Prepare XMPP client
config := xmpp.Config{
TransportConfiguration: xmpp.TransportConfiguration{
Address: *address,
},
Jid: *jid,
Credential: xmpp.Password(*password),
// StreamLogger: os.Stdout,
2019-06-19 02:19:49 -07:00
Insecure: true,
}
router := xmpp.NewRouter()
router.NewRoute().
Packet("message").
HandlerFunc(func(s xmpp.Sender, p stanza.Packet) {
2019-06-19 02:19:49 -07:00
handleMessage(s, p, player)
})
router.NewRoute().
Packet("iq").
HandlerFunc(func(s xmpp.Sender, p stanza.Packet) {
2019-06-19 02:19:49 -07:00
handleIQ(s, p, player)
})
client, err := xmpp.NewClient(config, router, errorHandler)
2019-06-19 02:19:49 -07:00
if err != nil {
log.Fatalf("%+v", err)
2016-03-14 03:31:31 -07:00
}
2019-06-19 02:19:49 -07:00
cm := xmpp.NewStreamManager(client, nil)
log.Fatal(cm.Run())
2016-03-14 03:31:31 -07:00
}
func errorHandler(err error) {
fmt.Println(err.Error())
}
2016-03-14 03:31:31 -07:00
func handleMessage(s xmpp.Sender, p stanza.Packet, player *mpg123.Player) {
msg, ok := p.(stanza.Message)
2019-06-19 02:19:49 -07:00
if !ok {
return
}
command := strings.Trim(msg.Body, " ")
2016-03-14 03:31:31 -07:00
if command == "stop" {
2019-06-19 02:19:49 -07:00
player.Stop()
2016-03-14 03:31:31 -07:00
} else {
2019-06-19 02:19:49 -07:00
playSCURL(player, command)
sendUserTune(s, "Radiohead", "Spectre")
2016-03-14 03:31:31 -07:00
}
}
func handleIQ(s xmpp.Sender, p stanza.Packet, player *mpg123.Player) {
iq, ok := p.(*stanza.IQ)
2019-06-19 02:19:49 -07:00
if !ok {
return
}
switch payload := iq.Payload.(type) {
2016-03-14 03:31:31 -07:00
// We support IOT Control IQ
case *stanza.ControlSet:
2016-03-14 03:31:31 -07:00
var url string
for _, element := range payload.Fields {
if element.XMLName.Local == "string" && element.Name == "url" {
url = strings.Trim(element.Value, " ")
break
}
}
2019-06-19 02:19:49 -07:00
playSCURL(player, url)
setResponse := new(stanza.ControlSetResponse)
// FIXME: Broken
reply := stanza.IQ{Attrs: stanza.Attrs{To: iq.From, Type: "result", Id: iq.Id}, Payload: setResponse}
_ = s.Send(&reply)
2016-03-14 03:31:31 -07:00
// TODO add Soundclound artist / title retrieval
2019-06-19 02:19:49 -07:00
sendUserTune(s, "Radiohead", "Spectre")
2016-03-14 03:31:31 -07:00
default:
2019-06-19 02:19:49 -07:00
_, _ = fmt.Fprintf(os.Stdout, "Other IQ Payload: %T\n", iq.Payload)
2016-03-14 03:31:31 -07:00
}
}
2019-06-19 02:19:49 -07:00
func sendUserTune(s xmpp.Sender, artist string, title string) {
rq, err := stanza.NewPublishItemRq("localhost",
"http://jabber.org/protocol/tune",
"",
stanza.Item{
XMLName: xml.Name{Space: "http://jabber.org/protocol/tune", Local: "tune"},
Any: &stanza.Node{
Nodes: []stanza.Node{
{
XMLName: xml.Name{Local: "artist"},
Content: artist,
},
{
XMLName: xml.Name{Local: "title"},
Content: title,
},
},
},
})
if err != nil {
fmt.Printf("failed to build the publish request : %s", err.Error())
return
}
_ = s.Send(rq)
2016-03-14 03:31:31 -07:00
}
func playSCURL(p *mpg123.Player, rawURL string) {
songID, _ := soundcloud.GetSongID(rawURL)
// TODO: Maybe we need to check the track itself to get the stream URL from reply ?
url := soundcloud.FormatStreamURL(songID)
_ = p.Play(strings.ReplaceAll(url, "YOUR_SOUNDCLOUD_CLIENTID", scClientID))
2016-03-14 03:31:31 -07:00
}
// TODO
// - Have a player API to play, play next, or add to queue
// - Have the ability to parse custom packet to play sound
// - Use PEP to display tunes status
// - Ability to "speak" messages