func (t *TestServer) QueueSong(stream playsource.Playsource_QueueSongServer) error { inbound := queueStream(stream) for { select { case req, ok := <-inbound: if !ok { return nil } // Perform lookup immediately if t.foundProbability < rand.Float64() { err := stream.Send(&playsource.QueueSongResponse{ SongId: req.Song.SongId, Queued: false, Found: false, }) if err == io.EOF { return nil } else if err != nil { return err } continue } // Check queue size. if int(atomic.LoadInt32(&t.queueSize)) >= t.maxQueueSize { log.Println("Exceeded queue") err := stream.Send(&playsource.QueueSongResponse{ SongId: req.Song.SongId, Queued: false, Found: true, }) if err == io.EOF { return nil } else if err != nil { return err } } // All good, go for the queue t.queue <- *req.Song case song := <-t.finished: log.Println("Sending back") err := stream.Send(&playsource.QueueSongResponse{ SongId: song.SongId, Finished: true, Found: true, }) if err == io.EOF { return nil } else if err != nil { return err } } } }
func queueStream(stream playsource.Playsource_QueueSongServer) <-chan playsource.QueueSongRequest { inbound := make(chan playsource.QueueSongRequest) go func() { defer close(inbound) for { req, err := stream.Recv() if err == io.EOF { log.Println("stream closed") return } else if err != nil { log.Println(err) return } log.Println("Received queue song:", req) inbound <- *req } }() return inbound }
func (m *MopidyServer) QueueSong(stream playsource.Playsource_QueueSongServer) error { log.Println("Client connected") select { case <-m.master: default: return errf(codes.Unavailable, "A master already exists") } defer func() { m.master <- struct{}{} }() atomic.StoreInt32(&m.queueSize, 0) inbound := queueStream(stream) session, err := NewMopidySession(m.client, 2*m.maxQueueSize, m.pollInterval) if err != nil { return err } defer session.Close() log.Println("Starting loop") for { select { case req, ok := <-inbound: if !ok { // Inbound channel was closed, which means the stream was closed. log.Println("Inbound channel closed") return nil } // Search for song args := mopidy.SearchArgs{ TrackName: []string{req.Song.Name}, Artist: req.Song.Artists, } searchResults, err := m.client.Search(args) if err != nil { log.Println("Search error:", err) return err } tracks := make([]mopidy.Track, 0) for _, r := range searchResults { tracks = append(tracks, r.Tracks...) } // Did we finy any results? if len(tracks) == 0 { err := stream.Send(&playsource.QueueSongResponse{ SongId: req.Song.SongId, Queued: false, Found: false, }) if err == io.EOF { return nil } else if err != nil { return err } continue } // Check server queue size. if int(atomic.LoadInt32(&m.queueSize)) >= m.maxQueueSize { log.Println("Internal queue size reached: ", atomic.LoadInt32(&m.queueSize)) atomic.AddInt32(&m.queueSize, -1) err := stream.Send(&playsource.QueueSongResponse{ SongId: req.Song.SongId, Queued: false, Found: true, }) if err == io.EOF { return nil } else if err != nil { return err } } // Just take the first result? tracksAdded, err := m.client.AddTracks(tracks[0:1]) if err != nil { return err } if len(tracksAdded) == 0 { err := stream.Send(&playsource.QueueSongResponse{ SongId: req.Song.SongId, Queued: false, Found: false, }) if err == io.EOF { return nil } else if err != nil { return err } continue } log.Println("Queuing:", req.Song) err = session.QueueSong(SongTrackPair{ Song: *req.Song, Track: tracks[0], }) if err != nil { log.Println("Error queueing song:", err) return err } // If we aren't playing (for whatever reason), make sure we play. state, err := m.client.CurrentState() if err != nil { return err } switch state { case mopidy.Stopped: err = m.client.Play() if err != nil { return err } break case mopidy.Paused: err = m.client.Resume() if err != nil { return err } break } atomic.AddInt32(&m.queueSize, 1) case song := <-session.FinishedChan(): log.Println("finished:", song) err := stream.Send(&playsource.QueueSongResponse{ SongId: song.Song.SongId, Finished: true, Found: true, }) if err == io.EOF { return nil } else if err != nil { return err } atomic.AddInt32(&m.queueSize, -1) } } return nil }