package cmd import ( "fmt" "os" "os/exec" "sync" "time" "github.com/dexterlb/mpvipc" ) type VideoID string type MPVPLayer struct { c *mpvipc.Connection f *os.File videoID VideoID videoPosition time.Duration videoDuration time.Duration isPlaying bool finishedVideo string sync.RWMutex } func NewMPVPlayer() *MPVPLayer { return &MPVPLayer{} } func (mpv *MPVPLayer) Exit() error { return nil } func (mpv *MPVPLayer) Play(id VideoID, url string) error { if err := mpv.ensurePlayer(); err != nil { return err } mpv.Lock() defer mpv.Unlock() _, err := mpv.c.Call("loadfile", url) if err != nil { return err } mpv.videoID = id mpv.isPlaying = true if err := mpv.c.Set("pause", false); err != nil { return err } return nil } func (mpv *MPVPLayer) Stop() error { mpv.Lock() defer mpv.Unlock() if mpv.c == nil { return nil } err := mpv.c.Set("pause", true) return err } func (mpv *MPVPLayer) VideoDuration() time.Duration { mpv.Lock() defer mpv.Unlock() if mpv.c != nil { data, _ := mpv.c.Get("duration") if data != nil { data_s := data.(float64) if data_s != 0 { mpv.videoDuration = time.Duration(data_s) * time.Second } } } return mpv.videoDuration } func (mpv *MPVPLayer) VideoPosition() time.Duration { mpv.Lock() defer mpv.Unlock() if mpv.c != nil { data, _ := mpv.c.Get("time-pos") if data != nil { data_s := data.(float64) if data_s != 0 { mpv.videoPosition = time.Duration(data_s) * time.Second } } } return mpv.videoPosition } func (mpv *MPVPLayer) IsPlaying() bool { mpv.Lock() defer mpv.Unlock() if mpv.c == nil { return false } // closed if mpv.c.IsClosed() { return false } // eof data, _ := mpv.c.Get("eof-reached") if data != nil && data.(bool) == true { return false } return mpv.isPlaying } // TODO: this fucking sucks, a lot, what the fuck func (mpv *MPVPLayer) ensurePlayer() error { mpv.Lock() defer mpv.Unlock() if mpv.c != nil && !mpv.c.IsClosed() { return nil } // TODO: cleanup this file var err error // mpv.f, err = os.Create(fmt.Sprintf("%s/%s", os.TempDir(), "mpvminiflux")) # no such device or address, etc. errors mpv.f, err = os.CreateTemp("", "mpvminiflux") if err != nil { return err } cmd := exec.Command( "mpv", "--keep-open=yes", "--idle", fmt.Sprintf("--input-ipc-server=%s", mpv.f.Name()), ) // start err = cmd.Start() if err != nil { return err } // try 10 times to open connection // return error otherwise for i := 0; i < 10; i++ { time.Sleep(10 * time.Millisecond) mpv.c = mpvipc.NewConnection(mpv.f.Name()) err = mpv.c.Open() if err == nil { break } } if err != nil { return err } return nil }