321 lines
5.8 KiB
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
|
|
}
|