156 lines
3.5 KiB
Go
156 lines
3.5 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"git.massive.box/massivebox/fcaster/fcast"
|
|
"io"
|
|
"math"
|
|
"net/http"
|
|
)
|
|
|
|
type Model struct {
|
|
Controller *Controller
|
|
DiscoveredDevices []fcast.DiscoveredHost
|
|
Connection *fcast.Connection
|
|
EventManager *fcast.EventManager
|
|
Time float32
|
|
Length float32
|
|
Volume float32
|
|
}
|
|
|
|
func (m *Model) DiscoverDevices() ([]string, error) {
|
|
devices, err := fcast.Discover()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m.DiscoveredDevices = devices
|
|
var names []string
|
|
for _, d := range m.DiscoveredDevices {
|
|
names = append(names, d.Name)
|
|
}
|
|
return names, nil
|
|
}
|
|
|
|
func (m *Model) ConnectToDevice(name string) error {
|
|
device := &fcast.DiscoveredHost{}
|
|
for _, d := range m.DiscoveredDevices {
|
|
if d.Name == name {
|
|
device = &d
|
|
}
|
|
}
|
|
if device.Name == "" {
|
|
return errors.New("device not found")
|
|
}
|
|
connection, err := fcast.Connect(device.IPv4)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.Connection = connection
|
|
return nil
|
|
}
|
|
|
|
func (m *Model) StartCast(mediaURL string) error {
|
|
|
|
m.EventManager = &fcast.EventManager{}
|
|
|
|
m.EventManager.SetHandler(fcast.PlaybackUpdateMessage{}, func(message fcast.Message) {
|
|
msg := message.(*fcast.PlaybackUpdateMessage)
|
|
m.Length = msg.Duration
|
|
m.Time = msg.Time
|
|
m.Controller.ReceiverResponse(RespPlaybackUpdate, msg.Duration, msg.Time)
|
|
})
|
|
m.EventManager.SetHandler(fcast.VolumeUpdateMessage{}, func(message fcast.Message) {
|
|
msg := message.(*fcast.VolumeUpdateMessage)
|
|
m.Volume = msg.Volume
|
|
})
|
|
m.EventManager.SetHandler(fcast.PlaybackErrorMessage{}, func(message fcast.Message) {
|
|
msg := message.(*fcast.PlaybackErrorMessage)
|
|
m.Controller.LogAndShowError(fmt.Errorf("playback error: %s", msg.Message))
|
|
})
|
|
|
|
if mediaURL != "" {
|
|
mediaType, err := getMediaType(mediaURL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = m.Connection.SendMessage(&fcast.PlayMessage{
|
|
Container: mediaType,
|
|
Url: mediaURL,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return m.Connection.ListenForMessages(m.EventManager)
|
|
|
|
}
|
|
|
|
func (m *Model) PlayerAction(action int, args ...float32) error {
|
|
|
|
var message fcast.Message
|
|
|
|
switch action {
|
|
case ActionPlay:
|
|
message = &fcast.ResumeMessage{}
|
|
break
|
|
case ActionPause:
|
|
message = &fcast.PauseMessage{}
|
|
break
|
|
case ActionSkipBack:
|
|
message = &fcast.SeekMessage{Time: int(m.Time - 10)}
|
|
break
|
|
case ActionSkipForward:
|
|
message = &fcast.SeekMessage{Time: int(m.Time + 10)}
|
|
break
|
|
case ActionStop:
|
|
message = &fcast.StopMessage{}
|
|
break
|
|
case ActionVolumeUp:
|
|
message = &fcast.SetVolumeMessage{Volume: m.Volume + 0.1}
|
|
break
|
|
case ActionVolumeDown:
|
|
message = &fcast.SetVolumeMessage{Volume: m.Volume - 0.1}
|
|
break
|
|
case ArgsActionSeek:
|
|
if math.Abs(float64(args[0]-m.Time)) < 2 {
|
|
return nil
|
|
}
|
|
message = &fcast.SeekMessage{Time: int(args[0])}
|
|
break
|
|
}
|
|
|
|
return m.Connection.SendMessage(message)
|
|
|
|
}
|
|
|
|
func getMediaType(url string) (string, error) {
|
|
client := &http.Client{}
|
|
|
|
req, err := http.NewRequest("GET", url, nil)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
req.Header.Set("Range", "bytes=0-512")
|
|
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent {
|
|
return "", fmt.Errorf("server returned unexpected status: %s", resp.Status)
|
|
}
|
|
|
|
buffer := make([]byte, 512)
|
|
n, err := resp.Body.Read(buffer)
|
|
if err != nil && err != io.EOF {
|
|
return "", err
|
|
}
|
|
|
|
mimeType := http.DetectContentType(buffer[:n])
|
|
return mimeType, nil
|
|
}
|