package cmd import ( "context" "fmt" "os" "os/exec" "sync" "time" "github.com/dexterlb/mpvipc" ) type MPVPLayer struct { entries []*feedEntry c *mpvipc.Connection f *os.File VideoPosition time.Duration VideoDuration time.Duration sync.RWMutex } func NewMPVPlayer() *MPVPLayer { return &MPVPLayer{} } func (mpv *MPVPLayer) Exit() error { return nil } func (mpv *MPVPLayer) Queue(e feedEntry) error { // TODO(eyJhb): implement checking mpv.entries = append(mpv.entries, &e) if len(mpv.entries) == 1 { return mpv.Play(e.Link) } return nil } func (mpv *MPVPLayer) Play(url string) error { if err := mpv.ensurePlayer(); err != nil { return err } t, err := mpv.c.Call("loadfile", url) fmt.Println(t) return err } func (mpv *MPVPLayer) Stop() error { err := mpv.c.Set("pause", true) return err } func (mpv *MPVPLayer) IsPlaying() (bool, error) { if mpv.c == nil || mpv.c.IsClosed() { return false, nil } path, err := mpv.c.Get("path") if err != nil { return false, err } return path != "", nil } func (mpv *MPVPLayer) finishedVideo() (bool, error) { mpv.Lock() defer mpv.Unlock() mpv.entries = mpv.entries[1:] if len(mpv.entries) == 1 { if err := mpv.Play(mpv.entries[0].Link); err != nil { return false, err } } return false, nil } func (mpv *MPVPLayer) ensurePlayer() error { if mpv.c != nil && !mpv.c.IsClosed() { return nil } var err error mpv.f, err = os.CreateTemp("", "example") if err != nil { return err } // close and delete file defer func() { if mpv.f == nil { return } filename := mpv.f.Name() mpv.f.Close() os.Remove(filename) }() // defer mpv.finishedVideo() ctx := context.TODO() cmd := exec.CommandContext(ctx, "mpv", "--keep-open=yes", "--idle", // fmt.Sprintf("--input-ipc-server=%d", mpv.f.Fd()), fmt.Sprintf("--input-ipc-server=%s", mpv.f.Name()), ) // start err = cmd.Start() if err != nil { return err } time.Sleep(100 * time.Millisecond) mpv.c = mpvipc.NewConnection(mpv.f.Name()) if err := mpv.c.Open(); err != nil { return err } // setup observe properties events, _ := mpv.c.NewEventListener() if _, err = mpv.c.Call("observe_property", 1, "duration"); err != nil { return err } if _, err = mpv.c.Call("observe_property", 2, "time-pos"); err != nil { return err } if _, err = mpv.c.Call("observe_property", 3, "eof-reached"); err != nil { return err } go func() { for event := range events { fmt.Println("event", event) mpv.Lock() if event.ID == 1 { if event.Data != nil { mpv.VideoDuration = time.Duration(event.Data.(float64)) * time.Second } } else if event.ID == 2 { if event.Data != nil { mpv.VideoPosition = time.Duration(event.Data.(float64)) * time.Second } } else if event.ID == 3 { if event.Data != nil { mpv.finishedVideo() } } mpv.Unlock() } }() return nil // return cmd.Wait() }