add mpv player w/ ipc support
This commit is contained in:
parent
a706a38252
commit
6ae627522c
5 changed files with 216 additions and 23 deletions
|
@ -87,7 +87,7 @@ func (mp *MinifluxPlayer) View() string {
|
||||||
return mp.player.View()
|
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) {
|
func (mp *MinifluxPlayer) FetchCategories(ctx context.Context) ([]*miniflux.Category, error) {
|
||||||
|
|
168
cmd/mpv.go
Normal file
168
cmd/mpv.go
Normal 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
2
go.mod
|
@ -11,6 +11,7 @@ require (
|
||||||
github.com/charmbracelet/lipgloss v1.0.0 // indirect
|
github.com/charmbracelet/lipgloss v1.0.0 // indirect
|
||||||
github.com/charmbracelet/x/ansi v0.4.5 // indirect
|
github.com/charmbracelet/x/ansi v0.4.5 // indirect
|
||||||
github.com/charmbracelet/x/term v0.2.1 // 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/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // 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/sync v0.9.0 // indirect
|
||||||
golang.org/x/sys v0.27.0 // indirect
|
golang.org/x/sys v0.27.0 // indirect
|
||||||
golang.org/x/text v0.11.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
|
miniflux.app v1.0.46 // indirect
|
||||||
)
|
)
|
||||||
|
|
4
go.sum
4
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 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
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/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 h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
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=
|
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/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 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
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 h1:2/xrbXiEoQlj/bAZ8MT+fPN1+gmYDYuXVCOhvSskns4=
|
||||||
miniflux.app v1.0.46/go.mod h1:YtEJIO1vMCvZgyzDbds7II0W/H7sGpo3auFCQscuMrE=
|
miniflux.app v1.0.46/go.mod h1:YtEJIO1vMCvZgyzDbds7II0W/H7sGpo3auFCQscuMrE=
|
||||||
|
|
63
main.go
63
main.go
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.fricloud.dk/eyjhb/minifluxmpv/cmd"
|
"git.fricloud.dk/eyjhb/minifluxmpv/cmd"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
@ -9,36 +10,19 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
fmt.Println(test())
|
||||||
// ipcc := mpv.NewIPCClient("/tmp/mpvsocket") // Lowlevel client
|
return
|
||||||
// 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
|
|
||||||
|
|
||||||
// init miniflux
|
// 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
|
// init miniflux player
|
||||||
m := cmd.MinifluxPlayer{
|
m := cmd.MinifluxPlayer{
|
||||||
MinifluxClient: client,
|
MinifluxClient: client,
|
||||||
DefaultCategory: "",
|
DefaultCategory: "",
|
||||||
MpvPath: "mpv",
|
MpvPath: "mpv",
|
||||||
|
|
||||||
|
CurrentView: cmd.ViewListFeedEntries,
|
||||||
}
|
}
|
||||||
|
|
||||||
// run tea program
|
// 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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue