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}