hathoris/sources/mpv/source.go

321 lines
5.8 KiB
Go

package mpv
import (
"fmt"
"log"
"os"
"os/exec"
"path"
"time"
"github.com/DexterLB/mpvipc"
"git.nemunai.re/nemunaire/hathoris/sources"
)
type MPVSource struct {
process *exec.Cmd
ipcSocketDir string
Name string
Options []string
File string
}
func init() {
sources.SoundSources["mpv-nig"] = &MPVSource{
Name: "Radio NIG",
File: "http://stream.syntheticfm.com:8030/stream",
}
sources.SoundSources["mpv-synthfm"] = &MPVSource{
Name: "Radio Synthetic FM",
File: "http://stream.syntheticfm.com:8040/stream",
}
sources.SoundSources["mpv-nrw"] = &MPVSource{
Name: "NewRetroWave",
File: "https://youtube.com/channel/UCD-4g5w1h8xQpLaNS_ghU4g/videos",
}
sources.SoundSources["mpv-abgt"] = &MPVSource{
Name: "ABGT",
File: "https://youtube.com/playlist?list=PL6RLee9oArCArCAjnOtZ17dlVZQxaHG8G",
}
}
func (s *MPVSource) GetName() string {
return s.Name
}
func (s *MPVSource) IsActive() bool {
return s.process != nil
}
func (s *MPVSource) IsEnabled() bool {
return s.process != nil
}
func (s *MPVSource) ipcSocket() string {
return path.Join(s.ipcSocketDir, "mpv.socket")
}
func (s *MPVSource) Enable() (err error) {
if s.process != nil {
return fmt.Errorf("Already running")
}
s.ipcSocketDir, err = os.MkdirTemp("", "hathoris")
opts := append([]string{"--no-video", "--no-terminal"}, s.Options...)
if s.ipcSocketDir != "" {
opts = append(opts, "--input-ipc-server="+s.ipcSocket(), "--pause")
}
opts = append(opts, s.File)
s.process = exec.Command("mpv", opts...)
if err = s.process.Start(); err != nil {
return
}
go func() {
err := s.process.Wait()
if err != nil {
s.process.Process.Kill()
}
if s.ipcSocketDir != "" {
os.RemoveAll(s.ipcSocketDir)
}
s.process = nil
}()
if s.ipcSocketDir != "" {
_, err = os.Stat(s.ipcSocket())
for i := 20; i >= 0 && err != nil; i-- {
time.Sleep(100 * time.Millisecond)
_, err = os.Stat(s.ipcSocket())
}
time.Sleep(200 * time.Millisecond)
conn := mpvipc.NewConnection(s.ipcSocket())
err = conn.Open()
for i := 20; i >= 0 && err != nil; i-- {
time.Sleep(100 * time.Millisecond)
err = conn.Open()
}
if err != nil {
return err
}
defer conn.Close()
_, err = conn.Get("media-title")
for err != nil {
time.Sleep(100 * time.Millisecond)
_, err = conn.Get("media-title")
}
conn.Set("ao-volume", 1)
err = conn.Set("pause", false)
if err != nil {
return err
}
var pfc interface{}
pfc, err = conn.Get("paused-for-cache")
for err == nil && !pfc.(bool) {
time.Sleep(250 * time.Millisecond)
pfc, err = conn.Get("paused-for-cache")
}
err = nil
s.FadeIn(conn, 3, 50)
}
return
}
func (s *MPVSource) FadeIn(conn *mpvipc.Connection, speed int, level int) {
volume, err := conn.Get("ao-volume")
if err != nil {
volume = 1.0
}
for i := int(volume.(float64)) + 1; i <= level; i += speed {
conn.Set("ao-volume", i)
time.Sleep(time.Duration(300/speed) * time.Millisecond)
}
}
func (s *MPVSource) FadeOut(conn *mpvipc.Connection, speed int) {
volume, err := conn.Get("ao-volume")
if err == nil {
for i := int(volume.(float64)) - 1; i > 0; i -= speed {
if conn.Set("ao-volume", i) == nil {
time.Sleep(time.Duration(300/speed) * time.Millisecond)
}
}
}
}
func (s *MPVSource) Disable() error {
if s.process != nil {
if s.process.Process != nil {
if s.ipcSocketDir != "" {
conn := mpvipc.NewConnection(s.ipcSocket())
err := conn.Open()
if err == nil {
s.FadeOut(conn, 3)
conn.Close()
}
}
s.process.Process.Kill()
}
}
return nil
}
func (s *MPVSource) CurrentlyPlaying() string {
if s.ipcSocketDir != "" {
conn := mpvipc.NewConnection(s.ipcSocket())
err := conn.Open()
if err != nil {
log.Println("Unable to open mpv socket:", err.Error())
return "!"
}
defer conn.Close()
title, err := conn.Get("media-title")
if err != nil {
log.Println("Unable to retrieve title:", err.Error())
return "!"
}
return title.(string)
}
return "-"
}
func (s *MPVSource) TogglePause(id string) error {
if s.ipcSocketDir == "" {
return fmt.Errorf("Not supported")
}
conn := mpvipc.NewConnection(s.ipcSocket())
err := conn.Open()
if err != nil {
return err
}
defer conn.Close()
paused, err := conn.Get("pause")
if err != nil {
return err
}
if !paused.(bool) {
s.FadeOut(conn, 5)
}
err = conn.Set("pause", !paused.(bool))
if err != nil {
return err
}
if paused.(bool) {
s.FadeIn(conn, 5, 50)
}
return nil
}
func (s *MPVSource) HasPlaylist() bool {
if s.ipcSocketDir == "" {
return false
}
conn := mpvipc.NewConnection(s.ipcSocket())
err := conn.Open()
if err != nil {
return false
}
defer conn.Close()
plistCount, err := conn.Get("playlist-count")
if err != nil {
return false
}
return plistCount.(float64) > 1
}
func (s *MPVSource) NextTrack() error {
if s.ipcSocketDir == "" {
return fmt.Errorf("Not supported")
}
conn := mpvipc.NewConnection(s.ipcSocket())
err := conn.Open()
if err != nil {
return err
}
defer conn.Close()
_, err = conn.Call("playlist-next", "weak")
if err != nil {
return err
}
return nil
}
func (s *MPVSource) NextRandomTrack() error {
if s.ipcSocketDir == "" {
return fmt.Errorf("Not supported")
}
conn := mpvipc.NewConnection(s.ipcSocket())
err := conn.Open()
if err != nil {
return err
}
defer conn.Close()
_, err = conn.Call("playlist-shuffle")
if err != nil {
return err
}
_, err = conn.Call("playlist-next", "weak")
if err != nil {
return err
}
_, err = conn.Call("playlist-unshuffle")
if err != nil {
return err
}
return nil
}
func (s *MPVSource) PreviousTrack() error {
if s.ipcSocketDir == "" {
return fmt.Errorf("Not supported")
}
conn := mpvipc.NewConnection(s.ipcSocket())
err := conn.Open()
if err != nil {
return err
}
defer conn.Close()
_, err = conn.Call("playlist-prev", "weak")
if err != nil {
return err
}
return nil
}