add mpv player w/ ipc support

This commit is contained in:
eyjhb 2025-02-04 19:29:03 +01:00
parent a706a38252
commit 6ae627522c
Signed by: eyjhb
GPG key ID: 609F508E3239F920
5 changed files with 216 additions and 23 deletions

View file

@ -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) {

168
cmd/mpv.go Normal file
View file

@ -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()
}

2
go.mod
View file

@ -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
)

4
go.sum
View file

@ -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=

63
main.go
View file

@ -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
}