// Can be launched with: // ./xmpp_jukebox -jid=test@localhost/jukebox -password=test -address=localhost:5222 package main import ( "flag" "fmt" "log" "os" "strings" "github.com/processone/mpg123" "github.com/processone/soundcloud" "gosrc.io/xmpp" "gosrc.io/xmpp/stanza" ) // 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") 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() // 1. Create mpg player player, err := mpg123.NewPlayer() if err != nil { log.Fatal(err) } // 2. Prepare XMPP client config := xmpp.Config{ TransportConfiguration: xmpp.TransportConfiguration{ Address: *address, }, Jid: *jid, Credential: xmpp.Password(*password), // StreamLogger: os.Stdout, Insecure: true, } router := xmpp.NewRouter() router.NewRoute(). Packet("message"). HandlerFunc(func(s xmpp.Sender, p stanza.Packet) { handleMessage(s, p, player) }) router.NewRoute(). Packet("message"). HandlerFunc(func(s xmpp.Sender, p stanza.Packet) { handleIQ(s, p, player) }) client, err := xmpp.NewClient(config, router) if err != nil { log.Fatalf("%+v", err) } cm := xmpp.NewStreamManager(client, nil) log.Fatal(cm.Run()) } func handleMessage(s xmpp.Sender, p stanza.Packet, player *mpg123.Player) { msg, ok := p.(stanza.Message) if !ok { return } command := strings.Trim(msg.Body, " ") if command == "stop" { player.Stop() } else { playSCURL(player, command) sendUserTune(s, "Radiohead", "Spectre") } } func handleIQ(s xmpp.Sender, p stanza.Packet, player *mpg123.Player) { iq, ok := p.(stanza.IQ) if !ok { return } switch payload := iq.Payload.(type) { // We support IOT Control IQ case *stanza.ControlSet: var url string for _, element := range payload.Fields { if element.XMLName.Local == "string" && element.Name == "url" { url = strings.Trim(element.Value, " ") break } } 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) // TODO add Soundclound artist / title retrieval sendUserTune(s, "Radiohead", "Spectre") default: _, _ = fmt.Fprintf(os.Stdout, "Other IQ Payload: %T\n", iq.Payload) } } func sendUserTune(s xmpp.Sender, artist string, title string) { tune := stanza.Tune{Artist: artist, Title: title} iq := stanza.NewIQ(stanza.Attrs{Type: stanza.IQTypeSet, Id: "usertune-1", Lang: "en"}) payload := stanza.PubSub{Publish: &stanza.Publish{Node: "http://jabber.org/protocol/tune", Item: stanza.Item{Tune: &tune}}} iq.Payload = &payload _ = s.Send(iq) } 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(url) } // 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