Add some code for position getting that I'm looking to use in 1.15

This commit is contained in:
Alex Ballas
2023-01-31 00:28:22 +02:00
parent b7d5ef477e
commit ff75e600b3
3 changed files with 168 additions and 0 deletions

View File

@@ -294,6 +294,24 @@ type getTransportInfoAction struct {
InstanceID string
}
type getPositionInfoEnvelope struct {
XMLName xml.Name `xml:"s:Envelope"`
Schema string `xml:"xmlns:s,attr"`
Encoding string `xml:"s:encodingStyle,attr"`
GetPositionInfoBody getPositionInfoBody `xml:"s:Body"`
}
type getPositionInfoBody struct {
XMLName xml.Name `xml:"s:Body"`
GetPositionInfoAction getPositionInfoAction `xml:"u:GetPositionInfo"`
}
type getPositionInfoAction struct {
XMLName xml.Name `xml:"u:GetPositionInfo"`
AVTransport string `xml:"xmlns:u,attr"`
InstanceID string
}
func setAVTransportSoapBuild(tvdata *TVPayload) ([]byte, error) {
mediaTypeSlice := strings.Split(tvdata.MediaType, "/")
seekflag := "00"
@@ -816,3 +834,26 @@ func getTransportInfoSoapBuild() ([]byte, error) {
return append(xmlStart, b...), nil
}
func getPositionInfoSoapBuild() ([]byte, error) {
d := getPositionInfoEnvelope{
XMLName: xml.Name{},
Schema: "http://schemas.xmlsoap.org/soap/envelope/",
Encoding: "http://schemas.xmlsoap.org/soap/encoding/",
GetPositionInfoBody: getPositionInfoBody{
XMLName: xml.Name{},
GetPositionInfoAction: getPositionInfoAction{
XMLName: xml.Name{},
AVTransport: "urn:schemas-upnp-org:service:AVTransport:1",
InstanceID: "0",
},
},
}
xmlStart := []byte(`<?xml version="1.0" encoding="utf-8"?>`)
b, err := xml.Marshal(d)
if err != nil {
return nil, fmt.Errorf("getPositionInfoSoapBuild Marshal error: %w", err)
}
return append(xmlStart, b...), nil
}

View File

@@ -290,3 +290,27 @@ func TestGetTransportInfoSoapBuild(t *testing.T) {
})
}
}
func TestGetPositionInfoSoapBuild(t *testing.T) {
tt := []struct {
name string
want string
}{
{
`getPositionInfoSoapBuildTest #1`,
`<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:GetPositionInfo xmlns:u="urn:schemas-upnp-org:service:AVTransport:1"><InstanceID>0</InstanceID></u:GetPositionInfo></s:Body></s:Envelope>`,
},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
out, err := getPositionInfoSoapBuild()
if err != nil {
t.Fatalf("%s: Failed to call getPositionInfoSoapBuild due to %s", tc.name, err.Error())
}
if string(out) != tc.want {
t.Fatalf("%s: got: %s, want: %s.", tc.name, out, tc.want)
}
})
}
}

View File

@@ -139,6 +139,28 @@ type getTransportInfoResponse struct {
} `xml:"Body"`
}
type getPositionInfoResponse struct {
XMLName xml.Name `xml:"Envelope"`
Text string `xml:",chardata"`
S string `xml:"s,attr"`
EncodingStyle string `xml:"encodingStyle,attr"`
Body struct {
Text string `xml:",chardata"`
GetPositionInfoResponse struct {
Text string `xml:",chardata"`
U string `xml:"u,attr"`
Track string `xml:"Track"`
TrackDuration string `xml:"TrackDuration"`
TrackMetaData string `xml:"TrackMetaData"`
TrackURI string `xml:"TrackURI"`
RelTime string `xml:"RelTime"`
AbsTime string `xml:"AbsTime"`
RelCount string `xml:"RelCount"`
AbsCount string `xml:"AbsCount"`
} `xml:"GetPositionInfoResponse"`
} `xml:"Body"`
}
func (p *TVPayload) setAVTransportSoapCall() error {
parsedURLtransport, err := url.Parse(p.ControlURL)
if err != nil {
@@ -1068,6 +1090,87 @@ func (p *TVPayload) GetTransportInfo() ([]string, error) {
return []string{state, status, speed}, nil
}
// GetPositionInfo .
func (p *TVPayload) GetPositionInfo() ([]string, error) {
if p == nil {
return nil, errors.New("GetPositionInfo, nil tvdata")
}
parsedURLtransport, err := url.Parse(p.ControlURL)
if err != nil {
p.Log().Error().Str("Method", "GetPositionInfo").Str("Action", "URL Parse").Err(err).Msg("")
return nil, fmt.Errorf("GetPositionInfo parse error: %w", err)
}
var xmlbuilder []byte
xmlbuilder, err = getPositionInfoSoapBuild()
if err != nil {
p.Log().Error().Str("Method", "GetPositionInfo").Str("Action", "Build").Err(err).Msg("")
return nil, fmt.Errorf("GetPositionInfo build error: %w", err)
}
client := &http.Client{}
req, err := http.NewRequest("POST", parsedURLtransport.String(), bytes.NewReader(xmlbuilder))
if err != nil {
p.Log().Error().Str("Method", "GetPositionInfo").Str("Action", "Prepare POST").Err(err).Msg("")
return nil, fmt.Errorf("GetPositionInfo POST error: %w", err)
}
req.Header = http.Header{
"SOAPAction": []string{`"urn:schemas-upnp-org:service:AVTransport:1#GetPositionInfo"`},
"content-type": []string{"text/xml"},
"charset": []string{"utf-8"},
"Connection": []string{"close"},
}
headerBytesReq, err := json.Marshal(req.Header)
if err != nil {
p.Log().Error().Str("Method", "GetPositionInfo").Str("Action", "Header Marshaling").Err(err).Msg("")
return nil, fmt.Errorf("GetPositionInfo Request Marshaling error: %w", err)
}
p.Log().Debug().
Str("Method", "GetPositionInfo").Str("Action", "Request").
RawJSON("Headers", headerBytesReq).
Msg(string(xmlbuilder))
res, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("GetPositionInfo Do POST error: %w", err)
}
defer res.Body.Close()
headerBytesRes, err := json.Marshal(res.Header)
if err != nil {
p.Log().Error().Str("Method", "GetPositionInfo").Str("Action", "Header Marshaling #2").Err(err).Msg("")
return nil, fmt.Errorf("GetPositionInfo Response Marshaling error: %w", err)
}
resBytes, err := io.ReadAll(res.Body)
if err != nil {
p.Log().Error().Str("Method", "GetPositionInfo").Str("Action", "Readall").Err(err).Msg("")
return nil, fmt.Errorf("GetPositionInfo Failed to read response: %w", err)
}
p.Log().Debug().
Str("Method", "GetPositionInfo").Str("Action", "Response").Str("Status Code", strconv.Itoa(res.StatusCode)).
RawJSON("Headers", headerBytesRes).
Msg(string(resBytes))
var respPositionInfo getPositionInfoResponse
if err := xml.Unmarshal(resBytes, &respPositionInfo); err != nil {
p.Log().Error().Str("Method", "GetPositionInfo").Str("Action", "Unmarshal").Err(err).Msg("")
return nil, fmt.Errorf("GetPositionInfo Failed to unmarshal response: %w", err)
}
r := respPositionInfo.Body.GetPositionInfoResponse
duration := r.TrackDuration
reltime := r.RelTime
return []string{duration, reltime}, nil
}
// SendtoTV is a higher level method that gracefully handles the various
// states when communicating with the DMR devices.
func (p *TVPayload) SendtoTV(action string) error {