Files
wehub-resource-sync ead81af521
CI / go (push) Waiting to run
CI / build (darwin, macos-14) (push) Waiting to run
CI / build (windows, windows-2025) (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Failing after 0s
chore: import upstream snapshot with attribution
2026-07-13 12:31:13 +08:00

71 lines
1.7 KiB
Go

package ipc
import (
"bufio"
"context"
"encoding/json"
"fmt"
"io"
"time"
)
// StreamBands holds one IPC connection open and writes one NDJSON line per
// tick containing the current visualizer bands and mode. It exits cleanly
// when ctx is cancelled or the server closes the socket.
func StreamBands(ctx context.Context, sockPath string, interval time.Duration, out io.Writer) error {
if interval <= 0 {
interval = 33 * time.Millisecond
}
conn, err := dialSocket(sockPath, 3*time.Second)
if err != nil {
if isSocketUnavailable(err) {
return fmt.Errorf("cliamp is not running (no socket at %s)", sockPath)
}
return fmt.Errorf("connect: %w", err)
}
defer conn.Close()
reqLine, err := json.Marshal(Request{Cmd: "bands"})
if err != nil {
return fmt.Errorf("marshal request: %w", err)
}
reqLine = append(reqLine, '\n')
scanner := bufio.NewScanner(conn)
scanner.Buffer(make([]byte, 64*1024), 1024*1024)
ticker := time.NewTicker(interval)
defer ticker.Stop()
// Reused per-frame buffer so we make one out.Write call per line.
frame := make([]byte, 0, 512)
for {
select {
case <-ctx.Done():
return nil
case <-ticker.C:
}
conn.SetWriteDeadline(time.Now().Add(2 * time.Second))
if _, err := conn.Write(reqLine); err != nil {
return fmt.Errorf("write: %w", err)
}
conn.SetReadDeadline(time.Now().Add(2 * time.Second))
if !scanner.Scan() {
if err := scanner.Err(); err != nil {
return fmt.Errorf("read: %w", err)
}
return nil
}
// Pass the response through as-is; it is already NDJSON.
frame = append(frame[:0], scanner.Bytes()...)
frame = append(frame, '\n')
if _, err := out.Write(frame); err != nil {
return err
}
}
}