diff --git a/cmd/cmd.go b/cmd/cmd.go index 74330e8..63640a3 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -87,7 +87,7 @@ func (mp *MinifluxPlayer) View() string { return mp.player.View() } - return lipgloss.JoinVertical(0.2, mp.feedEntries.View(), "", mp.player.View()) + return lipgloss.JoinVertical(lipgloss.Top, mp.feedEntries.View(), "", mp.player.View()) } func (mp *MinifluxPlayer) FetchCategories(ctx context.Context) ([]*miniflux.Category, error) { diff --git a/cmd/mpv.go b/cmd/mpv.go new file mode 100644 index 0000000..ce57993 --- /dev/null +++ b/cmd/mpv.go @@ -0,0 +1,168 @@ +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() +} diff --git a/go.mod b/go.mod index dc94cec..d1cbd3d 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/charmbracelet/lipgloss v1.0.0 // indirect github.com/charmbracelet/x/ansi v0.4.5 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect + github.com/dexterlb/mpvipc v0.0.0-20241005113212-7cdefca0e933 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -27,5 +28,6 @@ require ( golang.org/x/sync v0.9.0 // indirect golang.org/x/sys v0.27.0 // indirect golang.org/x/text v0.11.0 // indirect + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect miniflux.app v1.0.46 // indirect ) diff --git a/go.sum b/go.sum index 74beac9..6269359 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoC github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/dexterlb/mpvipc v0.0.0-20241005113212-7cdefca0e933 h1:r4hxcT6GBIA/j8Ox4OXI5MNgMKfR+9plcAWYi1OnmOg= +github.com/dexterlb/mpvipc v0.0.0-20241005113212-7cdefca0e933/go.mod h1:RkQWLNITKkXHLP7LXxZSgEq+uFWU25M5qW7qfEhL9Wc= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -59,5 +61,7 @@ golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= miniflux.app v1.0.46 h1:2/xrbXiEoQlj/bAZ8MT+fPN1+gmYDYuXVCOhvSskns4= miniflux.app v1.0.46/go.mod h1:YtEJIO1vMCvZgyzDbds7II0W/H7sGpo3auFCQscuMrE= diff --git a/main.go b/main.go index 882f51b..d94ef82 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "time" "git.fricloud.dk/eyjhb/minifluxmpv/cmd" tea "github.com/charmbracelet/bubbletea" @@ -9,36 +10,19 @@ import ( ) func main() { - - // ipcc := mpv.NewIPCClient("/tmp/mpvsocket") // Lowlevel client - // c := mpv.NewClient(ipcc) // Highlevel client, can also use RPCClient - - // c.Loadfile("https://www.youtube.com/watch?v=9dncyekT9d4", mpv.LoadFileModeReplace) - // // c.SetPause(true) - // // c.Seek(600, mpv.SeekModeAbsolute) - // // c.SetFullscreen(true) - // c.SetPause(false) - // // fmt.Println(c.Idle()) - // // time.Sleep(10 * time.Second) - // // fmt.Println(c.Idle()) - // for { - // pos, _ := c.Position() - // dur, _ := c.Duration() - // fmt.Printf("%f/%f\n", pos, dur) - - // time.Sleep(1 * time.Second) - - // } - // return + fmt.Println(test()) + return // init miniflux - client := miniflux.New("https://miniflux.fricloud.dk/v1/", "tFkVpjmSvePfGX6SBtcE61HSziALNAIeZ0eq5mkQOso=") + client := miniflux.New("https://miniflux.fricloud.dk/v1/", "F_61V4HkhhJjCF_hI12oDAYhgMY2769KAvOkqPsubqc=") // init miniflux player m := cmd.MinifluxPlayer{ MinifluxClient: client, DefaultCategory: "", MpvPath: "mpv", + + CurrentView: cmd.ViewListFeedEntries, } // run tea program @@ -47,3 +31,38 @@ func main() { } } + +func test() error { + p := cmd.NewMPVPlayer() + fmt.Println(p.IsPlaying()) + err := p.Play("https://www.youtube.com/watch?v=gCYcHz2k5x0") + if err != nil { + return err + } + + time.Sleep(10 * time.Second) + fmt.Println(p.IsPlaying()) + + // err = p.Play("https://www.youtube.com/watch?v=3d3ceC_EuC0") + // if err != nil { + // return err + // } + + // time.Sleep(10 * time.Second) + + // fmt.Println(p.VideoDuration) + // fmt.Println(p.VideoPosition) + + // time.Sleep(1 * time.Second) + + err = p.Stop() + if err != nil { + return err + } + + fmt.Println("Started playing") + time.Sleep(100 * time.Second) + + return nil + +}