telnet.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. package liquidsoap
  2. import (
  3. "bufio"
  4. "fmt"
  5. "net"
  6. "strings"
  7. )
  8. // Client represents a telnet/unix domain connection to Liquidsoap
  9. //
  10. // A Client can perform low-level operations, such as Command(), or high-level operations such as Outputs()
  11. type Client struct {
  12. Conn net.Conn
  13. }
  14. // Stream represents a liquidsoap stream. It contains information about the status and the nature of it
  15. type Stream struct {
  16. Enabled bool `json:"enabled"`
  17. Up bool `json:"up"`
  18. Type string `json:"type"`
  19. }
  20. // NewTelnet returns a liquidsoap.Client created using telnet on the given parameters
  21. func NewTelnet(host string, port int) (Client, error) {
  22. conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
  23. if err != nil {
  24. return Client{}, err
  25. }
  26. return Client{Conn: conn}, nil
  27. }
  28. // Close closes the connection
  29. func (c *Client) Close() {
  30. if c.Conn != nil {
  31. c.Conn.Write([]byte("quit\n"))
  32. c.Conn.Close()
  33. }
  34. }
  35. // Command run a command, wait for output response to come and returns it
  36. func (c *Client) Command(cmdline string) (string, error) {
  37. c.Conn.Write([]byte(cmdline + "\n"))
  38. var out, line string
  39. var err error
  40. reader := bufio.NewReader(c.Conn)
  41. for {
  42. if line, err = reader.ReadString('\n'); err != nil {
  43. return "", err
  44. }
  45. line = line[:len(line)-2] + "\n" // \r\n -> \n
  46. if line == "END\n" {
  47. return out, nil
  48. }
  49. out += line
  50. }
  51. }
  52. // Outputs will return a map of outputs that liquidsoap is handling.
  53. // An output is set to true if it is enabled
  54. func (c *Client) Outputs() (outputs map[string]Stream, err error) {
  55. outputs = make(map[string]Stream)
  56. var cmdout string
  57. cmdout, err = c.Command("list")
  58. if err != nil {
  59. return
  60. }
  61. lines := strings.Split(cmdout, "\n")
  62. for _, l := range lines {
  63. parts := strings.SplitN(l, " : ", 2)
  64. if len(parts) < 2 {
  65. continue
  66. }
  67. if strings.Index(parts[1], "output.") == 0 {
  68. name := strings.TrimSpace(parts[0])
  69. var stream Stream
  70. stream.Type = parts[1][7:]
  71. enabled, err := c.GetOutput(name)
  72. if err == nil {
  73. stream.Enabled = enabled
  74. }
  75. up, err := c.GetOutputUp(name)
  76. if err == nil {
  77. stream.Up = up
  78. }
  79. outputs[name] = stream
  80. }
  81. }
  82. return
  83. }
  84. // GetOutput checks whether an output is enabled
  85. func (c *Client) GetOutput(name string) (bool, error) {
  86. cmdout, err := c.Command(name + ".status")
  87. if err != nil {
  88. return false, err
  89. }
  90. return strings.TrimSpace(cmdout) == "on", nil
  91. }
  92. // GetOutput checks whether an output is up
  93. //
  94. // Please note that this relies on a custom convention proposed by direttoforo, where to every stream "x"
  95. // there is an interactive boolean variable "x_up" that has its state
  96. func (c *Client) GetOutputUp(name string) (bool, error) {
  97. return c.GetVar(name + "_up")
  98. }
  99. // GetVar reads an interactive variable
  100. func (c *Client) GetVar(name string) (bool, error) {
  101. cmdout, err := c.Command("var.get " + name)
  102. if err != nil {
  103. return false, err
  104. }
  105. cmdout = strings.TrimSpace(cmdout)
  106. switch cmdout {
  107. case "true":
  108. return true, nil
  109. case "false":
  110. return false, nil
  111. default:
  112. return false, fmt.Errorf("Variable %s not set", name)
  113. }
  114. }
  115. // SetOutput enables or disables an output
  116. func (c *Client) SetOutput(name string, enabled bool) error {
  117. action := "stop"
  118. if enabled {
  119. action = "start"
  120. }
  121. cmdout, err := c.Command(fmt.Sprintf("%s.%s", name, action))
  122. if err != nil {
  123. return err
  124. }
  125. if strings.TrimSpace(cmdout) != "OK" {
  126. return fmt.Errorf("Error: liquidsoap replied '%s'", cmdout)
  127. }
  128. return nil
  129. }