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 }