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 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
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/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
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/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
63
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
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue