package mpvipc import ( "context" "encoding/json" "errors" "net" "time" "git.milar.in/milarin/channel" ) type Command struct { Command []interface{} `json:"command"` } type Response[T any] struct { Data T `json:"data"` RequestID int `json:"request_id"` Error string `json:"error"` } type Event[T any] struct { Data T `json:"data"` ID int `json:"id"` Name string `json:"name"` Error string `json:"error"` Reason string `json:"reason"` } func IsReady(ctx context.Context, socket string) <-chan bool { out := make(chan bool, 1) go func() { defer close(out) ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() for { select { case <-ticker.C: conn, err := net.Dial("unix", socket) if err == nil { defer conn.Close() out <- true return } case <-ctx.Done(): out <- false return } } }() return out } func SendCommand[T any](socket string, cmd *Command) (*Response[T], error) { conn, err := net.Dial("unix", socket) if err != nil { return nil, err } defer conn.Close() if err := json.NewEncoder(conn).Encode(cmd); err != nil { return nil, err } resp := &Response[T]{} if err := json.NewDecoder(conn).Decode(resp); err != nil { return nil, err } return resp, nil } func GetProperty[T any](socket string, propertyName string) (T, error) { cmd := &Command{[]interface{}{"get_property", propertyName}} resp, err := SendCommand[T](socket, cmd) if err != nil { return *new(T), err } if resp.Error != "success" { return *new(T), errors.New(resp.Error) } return resp.Data, nil } func SetProperty[T any](socket string, propertyName string, propertyValue T) error { cmd := &Command{[]interface{}{"set_property", propertyName, propertyValue}} resp, err := SendCommand[T](socket, cmd) if err != nil { return err } if resp.Error != "success" { return errors.New(resp.Error) } return nil } func ObserveProperty[T any](ctx context.Context, socket string, propertyName string) (<-chan T, error) { ch, err := ObserveEvent[T](ctx, socket, propertyName) if err != nil { return nil, err } return channel.MapSuccessive(ch, func(e Event[T]) T { return e.Data }), nil } func ObserveEvent[T any](ctx context.Context, socket string, propertyName string) (<-chan Event[T], error) { out := make(chan Event[T], 10) conn, err := net.Dial("unix", socket) if err != nil { return nil, err } enc := json.NewEncoder(conn) dec := json.NewDecoder(conn) cmd := &Command{[]interface{}{"observe_property", 1, propertyName}} if err := enc.Encode(cmd); err != nil { conn.Close() return nil, err } resp := &Response[T]{} if err := json.NewDecoder(conn).Decode(resp); err != nil { conn.Close() return nil, err } if resp.Error != "success" { conn.Close() return nil, errors.New(resp.Error) } go func() { defer conn.Close() defer enc.Encode(&Command{[]interface{}{"unobserve_property", 0}}) <-ctx.Done() }() go func() { defer close(out) for ctx.Err() == nil { event := Event[T]{} if err := dec.Decode(&event); err != nil { break } out <- event } }() return out, nil }