Esempio n. 1
0
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
}