mpvfs

9P file server for controlling mpv playback

git clone https://git.8pit.net/mpvfs.git

  1package main
  2
  3import (
  4	"github.com/nmeum/mpvfs/fileserver"
  5	"github.com/nmeum/mpvfs/mpv"
  6	"github.com/nmeum/mpvfs/playlistfs"
  7
  8	"errors"
  9	"strings"
 10	"sync/atomic"
 11)
 12
 13var ErrEmptyPlaylist = errors.New("playlist is empty")
 14
 15type playctl struct {
 16	*playlistfs.BlockRecv
 17
 18	pos      chan int
 19	play     chan bool
 20	stopRead int32
 21
 22	state *playerState
 23	mpv   *mpv.Client
 24}
 25
 26func newCtl() (fileserver.File, error) {
 27	c := &playctl{
 28		state: state,
 29		mpv:   mpvClient,
 30		pos:   make(chan int, 1),
 31		play:  make(chan bool, 1),
 32	}
 33
 34	c.BlockRecv = playlistfs.NewBlockRecv(c)
 35	return c, nil
 36}
 37
 38func (c *playctl) StateReader(pos int, playing bool) *strings.Reader {
 39	var str string
 40	if pos == -1 {
 41		str = "stop"
 42	} else if playing {
 43		str = "play"
 44	} else {
 45		str = "pause"
 46	}
 47
 48	// XXX: This will set position to -1 on stop
 49	cmd := playlistfs.Control{Name: str, Arg: &pos}
 50	return strings.NewReader(cmd.String() + "\n")
 51}
 52
 53func (c *playctl) CurrentReader() *strings.Reader {
 54	reader := c.StateReader(c.state.Index(), c.state.Playing())
 55
 56	go func(ch chan<- int) {
 57		for atomic.LoadInt32(&c.stopRead) == 0 {
 58			ch <- c.state.WaitIndex()
 59		}
 60	}(c.pos)
 61	go func(ch chan<- bool) {
 62		for atomic.LoadInt32(&c.stopRead) == 0 {
 63			ch <- c.state.WaitPlaying()
 64		}
 65	}(c.play)
 66
 67	return reader
 68}
 69
 70func (c *playctl) NextReader() *strings.Reader {
 71	var pos int
 72	var playing bool
 73
 74	select {
 75	case i := <-c.pos:
 76		pos = i
 77		playing = c.state.Playing()
 78	case p := <-c.play:
 79		playing = p
 80		pos = c.state.Index()
 81	}
 82
 83	return c.StateReader(pos, playing)
 84}
 85
 86func (c *playctl) Write(off int64, p []byte) (int, error) {
 87	cmd, err := playlistfs.CtlCmd(p)
 88	if err != nil {
 89		return 0, err
 90	}
 91
 92	if cmd.Arg != nil {
 93		err := c.mpv.SetProperty("playlist-pos", *cmd.Arg)
 94		if err != nil {
 95			return 0, err
 96		}
 97	}
 98
 99	switch cmd.Name {
100	case "stop":
101		err := c.mpv.SetProperty("playlist-pos", -1)
102		if err != nil {
103			return 0, err
104		}
105
106		fallthrough
107	case "pause":
108		err := c.mpv.SetProperty("pause", true)
109		if err != nil {
110			return 0, err
111		}
112	case "skip":
113		var inc int
114		if cmd.Arg != nil {
115			inc = *cmd.Arg
116		} else {
117			inc = 1
118		}
119
120		idx := c.state.Index()
121		if idx == -1 {
122			inc = 1 // Start from beginning
123		}
124
125		newArg := idx + inc
126		cmd.Arg = &newArg
127
128		fallthrough
129	case "play":
130		fallthrough
131	case "resume":
132		err := c.mpv.SetProperty("pause", false)
133		if err != nil {
134			return 0, err
135		}
136	}
137
138	return len(p), nil
139}
140
141func (c *playctl) Close() error {
142	atomic.StoreInt32(&c.stopRead, 1)
143	return nil
144}