// Watcher instances and connects via channels the go routines that contain the actual logic. // It also triggers the logic by feeding mpd events to the go routine pipe. func Watcher(client *mpd.Client, cfg *Config) error { // Watch for 'player' events: w, err := mpd.NewWatcher("tcp", fmt.Sprintf("%s:%d", cfg.Host, cfg.Port), "", "player") if err != nil { log.Fatalf("Failed to create watcher: %v", err) return err } defer w.Close() // Log mpd errors, but don't handle them more than that: go func() { for err := range w.Error { log.Println("Error:", err) } }() // Watcher -> StatusUpdater updateCh := make(chan bool) // StatusUpdater -> MoodbarAdjuster eventCh := make(chan MPDEvent) // MoodbarAdjuster -> MoodbarRunner colorsCh := make(chan TimedColor) // Start the respective go routines: go MoodbarRunner(colorsCh) go MoodbarAdjuster(eventCh, colorsCh) go StatusUpdater(client, cfg, updateCh, eventCh) // Also sync extra every few seconds: go func() { for range time.NewTicker(2 * time.Second).C { updateCh <- true } }() // ..but directly react on a changed player event: go func() { for range w.Event { updateCh <- true } }() // Block until something fatal happens: sigint := make(chan os.Signal) signal.Notify(sigint, os.Interrupt) <-sigint // Attempt to clean up, this might not even be executed: close(colorsCh) close(eventCh) close(updateCh) return nil }
// Opens a connection to MPD. func (w *Watcher) dial() (watcher *mpd.Watcher, err error) { for { if watcher, err = mpd.NewWatcher("tcp", w.addr, w.passwd); err == nil { return } select { case <-w.done: return case <-time.After(time.Second): // retry } } }
func (m *MPD) watch() (err error) { m.watcher, err = mpd.NewWatcher("tcp", configuration.MPDConf.Host, "") if err != nil { glog.Errorf("mpd.watch: connect to %v error: %v", configuration.MPDConf.Host, err.Error()) return err } glog.Info("mpd.watch: connected to ", configuration.MPDConf.Host) glog.V(1).Infof("mpd.watch: starting watch") m.Message <- MPDGetStatus() for { if m.watcher == nil { break } select { case _ = <-m.end: glog.Info("mpd.watch: end") m.active = false return case subsystem := <-m.watcher.Event: if glog.V(1) { glog.Info("mpd.watch: event: ", subsystem) } m.Message <- MPDGetStatus() /* switch subsystem { case "player": m.Message <- MPDGetStatus() default: m.Message <- MPDGetStatus() } */ case err := <-m.watcher.Error: //glog.Errorf("mpd.watch: error event: %v", err) return err } } return nil }
func main() { conn, err := mpd.Dial("tcp", "127.0.0.1:6600") if err != nil { fmt.Println("Error: could not connect to MPD, exiting") os.Exit(1) } defer conn.Close() w, err := mpd.NewWatcher("tcp", "127.0.0.1:6600", "", "player") if err != nil { fmt.Println("Error: could not connect to MPD, exiting") os.Exit(1) } defer w.Close() h := newHub() go h.run() // Log errors. go func() { for err := range w.Error { log.Println("Error:", err) } }() //Control song transitions -- During this time, update the websockets go func() { var status mpd.Attrs for _ = range w.Event { status, err = conn.Status() if err != nil { //Connections seem to drop often, so reconnect when this happens fmt.Println("Couldn't get current status! Error: " + err.Error()) conn.Close() fmt.Println("Reconnecting...") conn, err = mpd.Dial("tcp", "127.0.0.1:6600") if err != nil { fmt.Println("Error: could not connect to MPD, exiting") os.Exit(1) } defer conn.Close() status, err = conn.Status() } pos, _ := strconv.ParseFloat(status["elapsed"], 64) fmt.Println(pos) if pos == 0.000 { //Stop us from getting into an infinite loop by waiting 25 ms time.Sleep(25 * time.Millisecond) //updateQueue <- &updateQueueMsg{Song: song["Title"], Artist: song["Artist"]} conn.Pause(true) //Wait 3 seconds then resume next song time.Sleep(3000 * time.Millisecond) conn.Pause(false) song, err := conn.CurrentSong() if err != nil { fmt.Println("Couldn't get current song! Error: " + err.Error()) } else { //Serialize and send info msg := map[string]string{"cmd": "NP", "Title": song["Title"], "Artist": song["Artist"], "Album": song["Album"], "Cover": "/art/" + GetAlbumDir(song["file"])} jsonMsg, _ := json.Marshal(msg) h.broadcast <- []byte(jsonMsg) } } } }() song, err := conn.CurrentSong() if err != nil { fmt.Println("No Song!") } else { fmt.Println(song["Title"]) } songs, err := conn.ListAllInfo("/") shuffle(songs) subset := songs[:20] //Searches for cover image web.Get("/art/(.+)", getCover) //Returns main page with custom selection of songs web.Get("/", func(ctx *web.Context) string { return getIndex(ctx, subset) }) //Returns a raw song web.Get("/song/(.+)", getSong) //Returns the JSON info for the currently playing song web.Get("/np", func(ctx *web.Context) string { song, _ := conn.CurrentSong() jsonMsg, _ := json.Marshal(song) return string(jsonMsg) }) //Handle the websocket web.Websocket("/ws", websocket.Handler(func(ws *websocket.Conn) { handleSocket(ws, h) })) web.Get("/library", func(ctx *web.Context) string { jsonMsg, _ := json.Marshal(subset) return string(jsonMsg) }) web.Run("0.0.0.0:8080") }