package liquidsoap import ( "bufio" "fmt" "net" "strings" ) // Client represents a telnet/unix domain connection to Liquidsoap // // A Client can perform low-level operations, such as Command(), or high-level operations such as Outputs() type Client struct { Conn net.Conn } // Stream represents a liquidsoap stream. It contains information about the status and the nature of it type Stream struct { Enabled bool `json:"enabled"` Up bool `json:"up"` Type string `json:"type"` } // NewTelnet returns a liquidsoap.Client created using telnet on the given parameters func NewTelnet(host string, port int) (Client, error) { conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", host, port)) if err != nil { return Client{}, err } return Client{Conn: conn}, nil } // Close closes the connection func (c *Client) Close() { if c.Conn != nil { c.Conn.Write([]byte("quit\n")) c.Conn.Close() } } // Command run a command, wait for output response to come and returns it func (c *Client) Command(cmdline string) (string, error) { c.Conn.Write([]byte(cmdline + "\n")) var out, line string var err error reader := bufio.NewReader(c.Conn) for { if line, err = reader.ReadString('\n'); err != nil { return "", err } line = line[:len(line)-2] + "\n" // \r\n -> \n if line == "END\n" { return out, nil } out += line } } // Outputs will return a map of outputs that liquidsoap is handling. // An output is set to true if it is enabled func (c *Client) Outputs() (outputs map[string]Stream, err error) { outputs = make(map[string]Stream) var cmdout string cmdout, err = c.Command("list") if err != nil { return } lines := strings.Split(cmdout, "\n") for _, l := range lines { parts := strings.SplitN(l, " : ", 2) if len(parts) < 2 { continue } if strings.Index(parts[1], "output.") == 0 { name := strings.TrimSpace(parts[0]) var stream Stream stream.Type = parts[1][7:] enabled, err := c.GetOutput(name) if err == nil { stream.Enabled = enabled } up, err := c.GetOutputUp(name) if err == nil { stream.Up = up } outputs[name] = stream } } return } // GetOutput checks whether an output is enabled func (c *Client) GetOutput(name string) (bool, error) { cmdout, err := c.Command(name + ".status") if err != nil { return false, err } return strings.TrimSpace(cmdout) == "on", nil } // GetOutput checks whether an output is up // // Please note that this relies on a custom convention proposed by direttoforo, where to every stream "x" // there is an interactive boolean variable "x_up" that has its state func (c *Client) GetOutputUp(name string) (bool, error) { return c.GetVar(name + "_up") } // GetVar reads an interactive variable func (c *Client) GetVar(name string) (bool, error) { cmdout, err := c.Command("var.get " + name) if err != nil { return false, err } cmdout = strings.TrimSpace(cmdout) switch cmdout { case "true": return true, nil case "false": return false, nil default: return false, fmt.Errorf("Variable %s not set", name) } } // SetOutput enables or disables an output func (c *Client) SetOutput(name string, enabled bool) error { action := "stop" if enabled { action = "start" } cmdout, err := c.Command(fmt.Sprintf("%s.%s", name, action)) if err != nil { return err } if strings.TrimSpace(cmdout) != "OK" { return fmt.Errorf("Error: liquidsoap replied '%s'", cmdout) } return nil }