PageRenderTime 45ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/mpd/client.go

https://code.google.com/p/gompd/
Go | 289 lines | 215 code | 32 blank | 42 comment | 62 complexity | b2e61f3b59753d458ff6be7507096786 MD5 | raw file
  1. // Copyright 2009 The GoMPD Authors. All rights reserved.
  2. // Use of this source code is governed by the MIT
  3. // license that can be found in the LICENSE file.
  4. // Package mpd provides the client side interface to MPD (Music Player Daemon).
  5. // The protocol reference can be found at http://www.musicpd.org/doc/protocol/index.html
  6. package mpd
  7. import (
  8. "errors"
  9. "net/textproto"
  10. "strconv"
  11. "strings"
  12. )
  13. type Client struct {
  14. text *textproto.Conn
  15. }
  16. type Attrs map[string]string
  17. // Dial connects to MPD listening on address addr (e.g. "127.0.0.1:6600")
  18. // on network network (e.g. "tcp").
  19. func Dial(network, addr string) (c *Client, err error) {
  20. text, err := textproto.Dial(network, addr)
  21. if err != nil {
  22. return nil, err
  23. }
  24. line, err := text.ReadLine()
  25. if err != nil {
  26. return nil, err
  27. }
  28. if line[0:6] != "OK MPD" {
  29. return nil, textproto.ProtocolError("no greeting")
  30. }
  31. return &Client{text: text}, nil
  32. }
  33. // Close terminates the connection with MPD.
  34. func (c *Client) Close() (err error) {
  35. if c.text != nil {
  36. c.text.PrintfLine("close")
  37. err = c.text.Close()
  38. c.text = nil
  39. }
  40. return
  41. }
  42. // Ping sends a no-op message to MPD. It's useful for keeping the connection alive.
  43. func (c *Client) Ping() error {
  44. return c.okCmd("ping")
  45. }
  46. func (c *Client) readPlaylist() (pls []Attrs, err error) {
  47. pls = []Attrs{}
  48. for {
  49. line, err := c.text.ReadLine()
  50. if err != nil {
  51. return nil, err
  52. }
  53. if line == "OK" {
  54. break
  55. }
  56. if strings.HasPrefix(line, "file:") { // new song entry begins
  57. pls = append(pls, Attrs{})
  58. }
  59. if len(pls) == 0 {
  60. return nil, textproto.ProtocolError("unexpected: " + line)
  61. }
  62. z := strings.Index(line, ": ")
  63. if z < 0 {
  64. return nil, textproto.ProtocolError("can't parse line: " + line)
  65. }
  66. key := line[0:z]
  67. pls[len(pls)-1][key] = line[z+2:]
  68. }
  69. return pls, nil
  70. }
  71. func (c *Client) readAttrs() (attrs Attrs, err error) {
  72. attrs = make(Attrs)
  73. for {
  74. line, err := c.text.ReadLine()
  75. if err != nil {
  76. return nil, err
  77. }
  78. if line == "OK" {
  79. break
  80. }
  81. z := strings.Index(line, ": ")
  82. if z < 0 {
  83. return nil, textproto.ProtocolError("can't parse line: " + line)
  84. }
  85. key := line[0:z]
  86. attrs[key] = line[z+2:]
  87. }
  88. return
  89. }
  90. // CurrentSong returns information about the current song in the playlist.
  91. func (c *Client) CurrentSong() (Attrs, error) {
  92. id, err := c.text.Cmd("currentsong")
  93. if err != nil {
  94. return nil, err
  95. }
  96. c.text.StartResponse(id)
  97. defer c.text.EndResponse(id)
  98. return c.readAttrs()
  99. }
  100. // Status returns information about the current status of MPD.
  101. func (c *Client) Status() (Attrs, error) {
  102. id, err := c.text.Cmd("status")
  103. if err != nil {
  104. return nil, err
  105. }
  106. c.text.StartResponse(id)
  107. defer c.text.EndResponse(id)
  108. return c.readAttrs()
  109. }
  110. func (c *Client) readOKLine() (err error) {
  111. line, err := c.text.ReadLine()
  112. if err != nil {
  113. return
  114. }
  115. if line == "OK" {
  116. return nil
  117. }
  118. return textproto.ProtocolError("unexpected response: " + line)
  119. }
  120. func (c *Client) okCmd(format string, args ...interface{}) error {
  121. id, err := c.text.Cmd(format, args...)
  122. if err != nil {
  123. return err
  124. }
  125. c.text.StartResponse(id)
  126. defer c.text.EndResponse(id)
  127. return c.readOKLine()
  128. }
  129. //
  130. // Playback control
  131. //
  132. // Next plays next song in the playlist.
  133. func (c *Client) Next() error {
  134. return c.okCmd("next")
  135. }
  136. // Pause pauses playback if pause is true; resumes playback otherwise.
  137. func (c *Client) Pause(pause bool) error {
  138. if pause {
  139. return c.okCmd("pause 1")
  140. }
  141. return c.okCmd("pause 0")
  142. }
  143. // Play starts playing the song at playlist position pos. If pos is negative,
  144. // start playing at the current position in the playlist.
  145. func (c *Client) Play(pos int) error {
  146. if pos < 0 {
  147. c.okCmd("play")
  148. }
  149. return c.okCmd("play %d", pos)
  150. }
  151. // PlayId plays the song identified by id. If id is negative, start playing
  152. // at the currect position in playlist.
  153. func (c *Client) PlayId(id int) error {
  154. if id < 0 {
  155. return c.okCmd("playid")
  156. }
  157. return c.okCmd("playid %d", id)
  158. }
  159. // Previous plays previous song in the playlist.
  160. func (c *Client) Previous() error {
  161. return c.okCmd("next")
  162. }
  163. // Seek seeks to the position time (in seconds) of the song at playlist position pos.
  164. func (c *Client) Seek(pos, time int) error {
  165. return c.okCmd("seek %d %d", pos, time)
  166. }
  167. // SeekId is identical to Seek except the song is identified by it's id
  168. // (not position in playlist).
  169. func (c *Client) SeekId(id, time int) error {
  170. return c.okCmd("seekid %d %d", id, time)
  171. }
  172. // Stop stops playback.
  173. func (c *Client) Stop() error {
  174. return c.okCmd("stop")
  175. }
  176. //
  177. // Playlist related functions
  178. //
  179. // PlaylistInfo returns attributes for songs in the current playlist. If
  180. // both start and end are negative, it does this for all songs in
  181. // playlist. If end is negative but start is positive, it does it for the
  182. // song at position start. If both start and end are positive, it does it
  183. // for positions in range [start, end).
  184. func (c *Client) PlaylistInfo(start, end int) (pls []Attrs, err error) {
  185. if start < 0 && end >= 0 {
  186. return nil, errors.New("negative start index")
  187. }
  188. if start >= 0 && end < 0 {
  189. id, err := c.text.Cmd("playlistinfo %d", start)
  190. if err != nil {
  191. return nil, err
  192. }
  193. c.text.StartResponse(id)
  194. defer c.text.EndResponse(id)
  195. return c.readPlaylist()
  196. }
  197. id, err := c.text.Cmd("playlistinfo")
  198. if err != nil {
  199. return nil, err
  200. }
  201. c.text.StartResponse(id)
  202. defer c.text.EndResponse(id)
  203. pls, err = c.readPlaylist()
  204. if err != nil || start < 0 || end < 0 {
  205. return
  206. }
  207. return pls[start:end], nil
  208. }
  209. // Delete deletes songs from playlist. If both start and end are positive,
  210. // it deletes those at positions in range [start, end). If end is negative,
  211. // it deletes the song at position start.
  212. func (c *Client) Delete(start, end int) error {
  213. if start < 0 {
  214. return errors.New("negative start index")
  215. }
  216. if end < 0 {
  217. return c.okCmd("delete %d", start)
  218. }
  219. return c.okCmd("delete %d %d", start, end)
  220. }
  221. // DeleteId deletes the song identified by id.
  222. func (c *Client) DeleteId(id int) error {
  223. return c.okCmd("deleteid %d", id)
  224. }
  225. // Add adds the file/directory uri to playlist. Directories add recursively.
  226. func (c *Client) Add(uri string) error {
  227. return c.okCmd("add %q", uri)
  228. }
  229. // AddId adds the file/directory uri to playlist and returns the identity
  230. // id of the song added. If pos is positive, the song is added to position
  231. // pos.
  232. func (c *Client) AddId(uri string, pos int) (int, error) {
  233. var id uint
  234. var err error
  235. if pos >= 0 {
  236. id, err = c.text.Cmd("addid %q %d", uri, pos)
  237. }
  238. id, err = c.text.Cmd("addid %q", uri)
  239. if err != nil {
  240. return -1, err
  241. }
  242. c.text.StartResponse(id)
  243. defer c.text.EndResponse(id)
  244. attrs, err := c.readAttrs()
  245. if err != nil {
  246. return -1, err
  247. }
  248. tok, ok := attrs["Id"]
  249. if !ok {
  250. return -1, textproto.ProtocolError("addid did not return Id")
  251. }
  252. return strconv.Atoi(tok)
  253. }
  254. // Clear clears the current playlist.
  255. func (c *Client) Clear() error {
  256. return c.okCmd("clear")
  257. }