905 lines
25 KiB
Go
905 lines
25 KiB
Go
package soapcalls
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/alexballas/go2tv/soapcalls/utils"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var (
|
|
ErrSetMuteInput = errors.New("setMuteSoapBuild input error. Was expecting 0 or 1.")
|
|
)
|
|
|
|
type playEnvelope struct {
|
|
XMLName xml.Name `xml:"s:Envelope"`
|
|
Schema string `xml:"xmlns:s,attr"`
|
|
Encoding string `xml:"s:encodingStyle,attr"`
|
|
PlayBody playBody `xml:"s:Body"`
|
|
}
|
|
|
|
type playBody struct {
|
|
XMLName xml.Name `xml:"s:Body"`
|
|
PlayAction playAction `xml:"u:Play"`
|
|
}
|
|
|
|
type playAction struct {
|
|
XMLName xml.Name `xml:"u:Play"`
|
|
AVTransport string `xml:"xmlns:u,attr"`
|
|
InstanceID string
|
|
Speed string
|
|
}
|
|
|
|
type pauseEnvelope struct {
|
|
XMLName xml.Name `xml:"s:Envelope"`
|
|
Schema string `xml:"xmlns:s,attr"`
|
|
Encoding string `xml:"s:encodingStyle,attr"`
|
|
PauseBody pauseBody `xml:"s:Body"`
|
|
}
|
|
|
|
type pauseBody struct {
|
|
XMLName xml.Name `xml:"s:Body"`
|
|
PauseAction pauseAction `xml:"u:Pause"`
|
|
}
|
|
|
|
type pauseAction struct {
|
|
XMLName xml.Name `xml:"u:Pause"`
|
|
AVTransport string `xml:"xmlns:u,attr"`
|
|
InstanceID string
|
|
Speed string
|
|
}
|
|
|
|
type stopEnvelope struct {
|
|
XMLName xml.Name `xml:"s:Envelope"`
|
|
Schema string `xml:"xmlns:s,attr"`
|
|
Encoding string `xml:"s:encodingStyle,attr"`
|
|
StopBody stopBody `xml:"s:Body"`
|
|
}
|
|
|
|
type stopBody struct {
|
|
XMLName xml.Name `xml:"s:Body"`
|
|
StopAction stopAction `xml:"u:Stop"`
|
|
}
|
|
|
|
type stopAction struct {
|
|
XMLName xml.Name `xml:"u:Stop"`
|
|
AVTransport string `xml:"xmlns:u,attr"`
|
|
InstanceID string
|
|
Speed string
|
|
}
|
|
|
|
type seekEnvelope struct {
|
|
XMLName xml.Name `xml:"s:Envelope"`
|
|
Schema string `xml:"xmlns:s,attr"`
|
|
Encoding string `xml:"s:encodingStyle,attr"`
|
|
SeekBody seekBody `xml:"s:Body"`
|
|
}
|
|
|
|
type seekBody struct {
|
|
XMLName xml.Name `xml:"s:Body"`
|
|
SeekAction seekAction `xml:"u:Seek"`
|
|
}
|
|
|
|
type seekAction struct {
|
|
XMLName xml.Name `xml:"u:Seek"`
|
|
AVTransport string `xml:"xmlns:u,attr"`
|
|
InstanceID string
|
|
Unit string
|
|
Target string
|
|
}
|
|
|
|
type setAVTransportEnvelope struct {
|
|
XMLName xml.Name `xml:"s:Envelope"`
|
|
Schema string `xml:"xmlns:s,attr"`
|
|
Encoding string `xml:"s:encodingStyle,attr"`
|
|
Body setAVTransportBody `xml:"s:Body"`
|
|
}
|
|
|
|
type setAVTransportBody struct {
|
|
XMLName xml.Name `xml:"s:Body"`
|
|
SetAVTransportURI setAVTransportURI `xml:"u:SetAVTransportURI"`
|
|
}
|
|
|
|
type setAVTransportURI struct {
|
|
XMLName xml.Name `xml:"u:SetAVTransportURI"`
|
|
AVTransport string `xml:"xmlns:u,attr"`
|
|
InstanceID string
|
|
CurrentURI string
|
|
CurrentURIMetaData currentURIMetaData `xml:"CurrentURIMetaData"`
|
|
}
|
|
|
|
type currentURIMetaData struct {
|
|
XMLName xml.Name `xml:"CurrentURIMetaData"`
|
|
Value []byte `xml:",chardata"`
|
|
}
|
|
|
|
type setNextAVTransportEnvelope struct {
|
|
XMLName xml.Name `xml:"s:Envelope"`
|
|
Schema string `xml:"xmlns:s,attr"`
|
|
Encoding string `xml:"s:encodingStyle,attr"`
|
|
Body setNextAVTransportBody `xml:"s:Body"`
|
|
}
|
|
|
|
type setNextAVTransportBody struct {
|
|
XMLName xml.Name `xml:"s:Body"`
|
|
SetNextAVTransportURI setNextAVTransportURI `xml:"u:SetNextAVTransportURI"`
|
|
}
|
|
|
|
type setNextAVTransportURI struct {
|
|
XMLName xml.Name `xml:"u:SetNextAVTransportURI"`
|
|
AVTransport string `xml:"xmlns:u,attr"`
|
|
InstanceID string
|
|
NextURI string
|
|
NextURIMetaData nextURIMetaData `xml:"NextURIMetaData"`
|
|
}
|
|
|
|
type nextURIMetaData struct {
|
|
XMLName xml.Name `xml:"NextURIMetaData"`
|
|
Value []byte `xml:",chardata"`
|
|
}
|
|
|
|
type didLLite struct {
|
|
XMLName xml.Name `xml:"DIDL-Lite"`
|
|
SchemaDIDL string `xml:"xmlns,attr"`
|
|
DC string `xml:"xmlns:dc,attr"`
|
|
Sec string `xml:"xmlns:sec,attr"`
|
|
SchemaUPNP string `xml:"xmlns:upnp,attr"`
|
|
DIDLLiteItem didLLiteItem `xml:"item"`
|
|
}
|
|
|
|
type didLLiteItem struct {
|
|
SecCaptionInfo *secCaptionInfo `xml:"sec:CaptionInfo,omitempty"`
|
|
SecCaptionInfoEx *secCaptionInfoEx `xml:"sec:CaptionInfoEx,omitempty"`
|
|
XMLName xml.Name `xml:"item"`
|
|
DCtitle string `xml:"dc:title"`
|
|
UPNPClass string `xml:"upnp:class"`
|
|
ID string `xml:"id,attr"`
|
|
ParentID string `xml:"parentID,attr"`
|
|
Restricted string `xml:"restricted,attr"`
|
|
ResNode []resNode `xml:"res"`
|
|
}
|
|
|
|
type resNode struct {
|
|
XMLName xml.Name `xml:"res"`
|
|
Duration string `xml:"duration,attr,omitempty"`
|
|
ProtocolInfo string `xml:"protocolInfo,attr"`
|
|
Value string `xml:",chardata"`
|
|
}
|
|
|
|
type secCaptionInfo struct {
|
|
XMLName xml.Name `xml:"sec:CaptionInfo"`
|
|
Type string `xml:"sec:type,attr"`
|
|
Value string `xml:",chardata"`
|
|
}
|
|
|
|
type secCaptionInfoEx struct {
|
|
XMLName xml.Name `xml:"sec:CaptionInfoEx"`
|
|
Type string `xml:"sec:type,attr"`
|
|
Value string `xml:",chardata"`
|
|
}
|
|
|
|
type setMuteEnvelope struct {
|
|
XMLName xml.Name `xml:"s:Envelope"`
|
|
Schema string `xml:"xmlns:s,attr"`
|
|
Encoding string `xml:"s:encodingStyle,attr"`
|
|
SetMuteBody setMuteBody `xml:"s:Body"`
|
|
}
|
|
|
|
type setMuteBody struct {
|
|
XMLName xml.Name `xml:"s:Body"`
|
|
SetMuteAction setMuteAction `xml:"u:SetMute"`
|
|
}
|
|
|
|
type setMuteAction struct {
|
|
XMLName xml.Name `xml:"u:SetMute"`
|
|
RenderingControl string `xml:"xmlns:u,attr"`
|
|
InstanceID string
|
|
Channel string
|
|
DesiredMute string
|
|
}
|
|
|
|
type getMuteEnvelope struct {
|
|
XMLName xml.Name `xml:"s:Envelope"`
|
|
Schema string `xml:"xmlns:s,attr"`
|
|
Encoding string `xml:"s:encodingStyle,attr"`
|
|
GetMuteBody getMuteBody `xml:"s:Body"`
|
|
}
|
|
|
|
type getMuteBody struct {
|
|
XMLName xml.Name `xml:"s:Body"`
|
|
GetMuteAction getMuteAction `xml:"u:GetMute"`
|
|
}
|
|
|
|
type getMuteAction struct {
|
|
XMLName xml.Name `xml:"u:GetMute"`
|
|
RenderingControl string `xml:"xmlns:u,attr"`
|
|
InstanceID string
|
|
Channel string
|
|
}
|
|
|
|
type getVolumeEnvelope struct {
|
|
XMLName xml.Name `xml:"s:Envelope"`
|
|
Schema string `xml:"xmlns:s,attr"`
|
|
Encoding string `xml:"s:encodingStyle,attr"`
|
|
GetVolumeBody getVolumeBody `xml:"s:Body"`
|
|
}
|
|
|
|
type getVolumeBody struct {
|
|
XMLName xml.Name `xml:"s:Body"`
|
|
GetVolumeAction getVolumeAction `xml:"u:GetVolume"`
|
|
}
|
|
|
|
type getVolumeAction struct {
|
|
XMLName xml.Name `xml:"u:GetVolume"`
|
|
RenderingControl string `xml:"xmlns:u,attr"`
|
|
InstanceID string
|
|
Channel string
|
|
}
|
|
|
|
type setVolumeEnvelope struct {
|
|
XMLName xml.Name `xml:"s:Envelope"`
|
|
Schema string `xml:"xmlns:s,attr"`
|
|
Encoding string `xml:"s:encodingStyle,attr"`
|
|
SetVolumeBody setVolumeBody `xml:"s:Body"`
|
|
}
|
|
|
|
type setVolumeBody struct {
|
|
XMLName xml.Name `xml:"s:Body"`
|
|
SetVolumeAction setVolumeAction `xml:"u:SetVolume"`
|
|
}
|
|
|
|
type setVolumeAction struct {
|
|
XMLName xml.Name `xml:"u:SetVolume"`
|
|
RenderingControl string `xml:"xmlns:u,attr"`
|
|
InstanceID string
|
|
Channel string
|
|
DesiredVolume string
|
|
}
|
|
|
|
type getProtocolInfoEnvelope struct {
|
|
XMLName xml.Name `xml:"s:Envelope"`
|
|
Schema string `xml:"xmlns:s,attr"`
|
|
Encoding string `xml:"s:encodingStyle,attr"`
|
|
GetProtocolInfoBody getProtocolInfoBody `xml:"s:Body"`
|
|
}
|
|
|
|
type getProtocolInfoBody struct {
|
|
XMLName xml.Name `xml:"s:Body"`
|
|
GetProtocolInfoAction getProtocolInfoAction `xml:"u:GetProtocolInfo"`
|
|
}
|
|
|
|
type getProtocolInfoAction struct {
|
|
XMLName xml.Name `xml:"u:GetProtocolInfo"`
|
|
ConnectionManager string `xml:"xmlns:u,attr"`
|
|
}
|
|
|
|
type getMediaInfoEnvelope struct {
|
|
XMLName xml.Name `xml:"s:Envelope"`
|
|
Schema string `xml:"xmlns:s,attr"`
|
|
Encoding string `xml:"s:encodingStyle,attr"`
|
|
GetMediaInfoBody getMediaInfoBody `xml:"s:Body"`
|
|
}
|
|
|
|
type getMediaInfoBody struct {
|
|
XMLName xml.Name `xml:"s:Body"`
|
|
GetMediaInfoAction getMediaInfoAction `xml:"u:GetMediaInfo"`
|
|
}
|
|
|
|
type getMediaInfoAction struct {
|
|
XMLName xml.Name `xml:"u:GetMediaInfo"`
|
|
AVTransport string `xml:"xmlns:u,attr"`
|
|
InstanceID string
|
|
}
|
|
|
|
type getTransportInfoEnvelope struct {
|
|
XMLName xml.Name `xml:"s:Envelope"`
|
|
Schema string `xml:"xmlns:s,attr"`
|
|
Encoding string `xml:"s:encodingStyle,attr"`
|
|
GetTransportInfoBody getTransportInfoBody `xml:"s:Body"`
|
|
}
|
|
|
|
type getTransportInfoBody struct {
|
|
XMLName xml.Name `xml:"s:Body"`
|
|
GetTransportInfoAction getTransportInfoAction `xml:"u:GetTransportInfo"`
|
|
}
|
|
|
|
type getTransportInfoAction struct {
|
|
XMLName xml.Name `xml:"u:GetTransportInfo"`
|
|
AVTransport string `xml:"xmlns:u,attr"`
|
|
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"
|
|
if tvdata.Seekable {
|
|
seekflag = "01"
|
|
}
|
|
|
|
contentFeatures, err := utils.BuildContentFeatures(tvdata.MediaType, seekflag, tvdata.Transcode)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("setAVTransportSoapBuild failed to build contentFeatures: %w", err)
|
|
}
|
|
|
|
var class string
|
|
switch mediaTypeSlice[0] {
|
|
case "audio":
|
|
class = "object.item.audioItem.musicTrack"
|
|
case "image":
|
|
class = "object.item.imageItem.photo"
|
|
default:
|
|
class = "object.item.videoItem.movie"
|
|
}
|
|
|
|
mediaTitlefromURL, err := url.Parse(tvdata.MediaURL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("setAVTransportSoapBuild url parse error: %w", err)
|
|
}
|
|
|
|
mediaTitle := strings.TrimLeft(mediaTitlefromURL.Path, "/")
|
|
|
|
re, err := regexp.Compile(`[&<>\\]+`)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("setAVTransportSoapBuild regex compile error: %w", err)
|
|
}
|
|
mediaTitle = re.ReplaceAllString(mediaTitle, "")
|
|
|
|
var didl didLLiteItem
|
|
resNodeData := []resNode{}
|
|
duration, _ := utils.DurationForMedia(tvdata.FFmpegPath, tvdata.MediaPath)
|
|
|
|
switch duration {
|
|
case "":
|
|
resNodeData = append(resNodeData, resNode{
|
|
XMLName: xml.Name{},
|
|
ProtocolInfo: fmt.Sprintf("http-get:*:%s:%s", tvdata.MediaType, contentFeatures),
|
|
Value: tvdata.MediaURL,
|
|
})
|
|
default:
|
|
resNodeData = append(resNodeData, resNode{
|
|
XMLName: xml.Name{},
|
|
Duration: duration,
|
|
ProtocolInfo: fmt.Sprintf("http-get:*:%s:%s", tvdata.MediaType, contentFeatures),
|
|
Value: tvdata.MediaURL,
|
|
})
|
|
}
|
|
|
|
didl = didLLiteItem{
|
|
XMLName: xml.Name{},
|
|
ID: "1",
|
|
ParentID: "0",
|
|
Restricted: "1",
|
|
UPNPClass: class,
|
|
DCtitle: mediaTitle,
|
|
ResNode: resNodeData,
|
|
}
|
|
|
|
if strings.Contains(tvdata.SubtitlesURL, "srt") {
|
|
resNodeData = append(resNodeData, resNode{
|
|
XMLName: xml.Name{},
|
|
ProtocolInfo: "http-get:*:text/srt:*",
|
|
Value: tvdata.SubtitlesURL,
|
|
})
|
|
|
|
didl = didLLiteItem{
|
|
XMLName: xml.Name{},
|
|
ID: "1",
|
|
ParentID: "0",
|
|
Restricted: "1",
|
|
DCtitle: mediaTitle,
|
|
UPNPClass: class,
|
|
ResNode: resNodeData,
|
|
SecCaptionInfo: &secCaptionInfo{
|
|
XMLName: xml.Name{},
|
|
Type: "srt",
|
|
Value: tvdata.SubtitlesURL,
|
|
},
|
|
SecCaptionInfoEx: &secCaptionInfoEx{
|
|
XMLName: xml.Name{},
|
|
Type: "srt",
|
|
Value: tvdata.SubtitlesURL,
|
|
},
|
|
}
|
|
}
|
|
|
|
l := didLLite{
|
|
XMLName: xml.Name{},
|
|
SchemaDIDL: "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/",
|
|
DC: "http://purl.org/dc/elements/1.1/",
|
|
Sec: "http://www.sec.co.kr/",
|
|
SchemaUPNP: "urn:schemas-upnp-org:metadata-1-0/upnp/",
|
|
DIDLLiteItem: didl,
|
|
}
|
|
|
|
a, err := xml.Marshal(l)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("setAVTransportSoapBuild #1 Marshal error: %w", err)
|
|
}
|
|
|
|
d := setAVTransportEnvelope{
|
|
XMLName: xml.Name{},
|
|
Schema: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Encoding: "http://schemas.xmlsoap.org/soap/encoding/",
|
|
Body: setAVTransportBody{
|
|
XMLName: xml.Name{},
|
|
SetAVTransportURI: setAVTransportURI{
|
|
XMLName: xml.Name{},
|
|
AVTransport: "urn:schemas-upnp-org:service:AVTransport:1",
|
|
InstanceID: "0",
|
|
CurrentURI: tvdata.MediaURL,
|
|
CurrentURIMetaData: currentURIMetaData{
|
|
XMLName: xml.Name{},
|
|
Value: a,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
xmlStart := []byte(`<?xml version="1.0" encoding="utf-8"?>`)
|
|
b, err := xml.Marshal(d)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("setAVTransportSoapBuild #2 Marshal error: %w", err)
|
|
}
|
|
|
|
// Samsung TV hack.
|
|
b = bytes.ReplaceAll(b, []byte("""), []byte(`"`))
|
|
b = bytes.ReplaceAll(b, []byte("&"), []byte("&"))
|
|
|
|
return append(xmlStart, b...), nil
|
|
}
|
|
|
|
func setNextAVTransportSoapBuild(tvdata *TVPayload, clear bool) ([]byte, error) {
|
|
mediaTypeSlice := strings.Split(tvdata.MediaType, "/")
|
|
seekflag := "00"
|
|
if tvdata.Seekable {
|
|
seekflag = "01"
|
|
}
|
|
|
|
contentFeatures, err := utils.BuildContentFeatures(tvdata.MediaType, seekflag, tvdata.Transcode)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("setNextAVTransportSoapBuild failed to build contentFeatures: %w", err)
|
|
}
|
|
|
|
var class string
|
|
switch mediaTypeSlice[0] {
|
|
case "audio":
|
|
class = "object.item.audioItem.musicTrack"
|
|
case "image":
|
|
class = "object.item.imageItem.photo"
|
|
default:
|
|
class = "object.item.videoItem.movie"
|
|
}
|
|
|
|
murl := tvdata.MediaURL
|
|
if clear {
|
|
murl = ""
|
|
}
|
|
|
|
mediaTitlefromURL, err := url.Parse(murl)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("setNextAVTransportSoapBuild url parse error: %w", err)
|
|
}
|
|
|
|
mediaTitle := strings.TrimLeft(mediaTitlefromURL.Path, "/")
|
|
|
|
re, err := regexp.Compile(`[&<>\\]+`)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("setNextAVTransportSoapBuild regex compile error: %w", err)
|
|
}
|
|
mediaTitle = re.ReplaceAllString(mediaTitle, "")
|
|
|
|
var didl didLLiteItem
|
|
resNodeData := []resNode{}
|
|
duration, _ := utils.DurationForMedia(tvdata.FFmpegPath, tvdata.MediaPath)
|
|
|
|
switch duration {
|
|
case "":
|
|
resNodeData = append(resNodeData, resNode{
|
|
XMLName: xml.Name{},
|
|
ProtocolInfo: fmt.Sprintf("http-get:*:%s:%s", tvdata.MediaType, contentFeatures),
|
|
Value: murl,
|
|
})
|
|
default:
|
|
resNodeData = append(resNodeData, resNode{
|
|
XMLName: xml.Name{},
|
|
Duration: duration,
|
|
ProtocolInfo: fmt.Sprintf("http-get:*:%s:%s", tvdata.MediaType, contentFeatures),
|
|
Value: murl,
|
|
})
|
|
}
|
|
|
|
didl = didLLiteItem{
|
|
XMLName: xml.Name{},
|
|
ID: "1",
|
|
ParentID: "0",
|
|
Restricted: "1",
|
|
UPNPClass: class,
|
|
DCtitle: mediaTitle,
|
|
ResNode: resNodeData,
|
|
}
|
|
|
|
if strings.Contains(tvdata.SubtitlesURL, "srt") {
|
|
resNodeData = append(resNodeData, resNode{
|
|
XMLName: xml.Name{},
|
|
ProtocolInfo: "http-get:*:text/srt:*",
|
|
Value: tvdata.SubtitlesURL,
|
|
})
|
|
|
|
didl = didLLiteItem{
|
|
XMLName: xml.Name{},
|
|
ID: "1",
|
|
ParentID: "0",
|
|
Restricted: "1",
|
|
DCtitle: mediaTitle,
|
|
UPNPClass: class,
|
|
ResNode: resNodeData,
|
|
SecCaptionInfo: &secCaptionInfo{
|
|
XMLName: xml.Name{},
|
|
Type: "srt",
|
|
Value: tvdata.SubtitlesURL,
|
|
},
|
|
SecCaptionInfoEx: &secCaptionInfoEx{
|
|
XMLName: xml.Name{},
|
|
Type: "srt",
|
|
Value: tvdata.SubtitlesURL,
|
|
},
|
|
}
|
|
}
|
|
|
|
l := didLLite{
|
|
XMLName: xml.Name{},
|
|
SchemaDIDL: "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/",
|
|
DC: "http://purl.org/dc/elements/1.1/",
|
|
Sec: "http://www.sec.co.kr/",
|
|
SchemaUPNP: "urn:schemas-upnp-org:metadata-1-0/upnp/",
|
|
DIDLLiteItem: didl,
|
|
}
|
|
|
|
a, err := xml.Marshal(l)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("setNextAVTransportSoapBuild #1 Marshal error: %w", err)
|
|
}
|
|
|
|
d := setNextAVTransportEnvelope{
|
|
XMLName: xml.Name{},
|
|
Schema: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Encoding: "http://schemas.xmlsoap.org/soap/encoding/",
|
|
Body: setNextAVTransportBody{
|
|
XMLName: xml.Name{},
|
|
SetNextAVTransportURI: setNextAVTransportURI{
|
|
XMLName: xml.Name{},
|
|
AVTransport: "urn:schemas-upnp-org:service:AVTransport:1",
|
|
InstanceID: "0",
|
|
NextURI: murl,
|
|
NextURIMetaData: nextURIMetaData{
|
|
XMLName: xml.Name{},
|
|
Value: a,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
xmlStart := []byte(`<?xml version="1.0" encoding="utf-8"?>`)
|
|
b, err := xml.Marshal(d)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("setNextAVTransportSoapBuild #2 Marshal error: %w", err)
|
|
}
|
|
|
|
// Samsung TV hack.
|
|
b = bytes.ReplaceAll(b, []byte("""), []byte(`"`))
|
|
b = bytes.ReplaceAll(b, []byte("&"), []byte("&"))
|
|
|
|
return append(xmlStart, b...), nil
|
|
}
|
|
|
|
func playSoapBuild() ([]byte, error) {
|
|
d := playEnvelope{
|
|
XMLName: xml.Name{},
|
|
Schema: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Encoding: "http://schemas.xmlsoap.org/soap/encoding/",
|
|
PlayBody: playBody{
|
|
XMLName: xml.Name{},
|
|
PlayAction: playAction{
|
|
XMLName: xml.Name{},
|
|
AVTransport: "urn:schemas-upnp-org:service:AVTransport:1",
|
|
InstanceID: "0",
|
|
Speed: "1",
|
|
},
|
|
},
|
|
}
|
|
xmlStart := []byte(`<?xml version="1.0" encoding="utf-8"?>`)
|
|
b, err := xml.Marshal(d)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("playSoapBuild Marshal error: %w", err)
|
|
}
|
|
|
|
return append(xmlStart, b...), nil
|
|
}
|
|
|
|
func stopSoapBuild() ([]byte, error) {
|
|
d := stopEnvelope{
|
|
XMLName: xml.Name{},
|
|
Schema: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Encoding: "http://schemas.xmlsoap.org/soap/encoding/",
|
|
StopBody: stopBody{
|
|
XMLName: xml.Name{},
|
|
StopAction: stopAction{
|
|
XMLName: xml.Name{},
|
|
AVTransport: "urn:schemas-upnp-org:service:AVTransport:1",
|
|
InstanceID: "0",
|
|
Speed: "1",
|
|
},
|
|
},
|
|
}
|
|
xmlStart := []byte(`<?xml version="1.0" encoding="utf-8"?>`)
|
|
b, err := xml.Marshal(d)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("stopSoapBuild Marshal error: %w", err)
|
|
}
|
|
|
|
return append(xmlStart, b...), nil
|
|
}
|
|
|
|
func pauseSoapBuild() ([]byte, error) {
|
|
d := pauseEnvelope{
|
|
XMLName: xml.Name{},
|
|
Schema: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Encoding: "http://schemas.xmlsoap.org/soap/encoding/",
|
|
PauseBody: pauseBody{
|
|
XMLName: xml.Name{},
|
|
PauseAction: pauseAction{
|
|
XMLName: xml.Name{},
|
|
AVTransport: "urn:schemas-upnp-org:service:AVTransport:1",
|
|
InstanceID: "0",
|
|
Speed: "1",
|
|
},
|
|
},
|
|
}
|
|
xmlStart := []byte(`<?xml version="1.0" encoding="utf-8"?>`)
|
|
b, err := xml.Marshal(d)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("pauseSoapBuild Marshal error: %w", err)
|
|
}
|
|
|
|
return append(xmlStart, b...), nil
|
|
}
|
|
|
|
func setMuteSoapBuild(m string) ([]byte, error) {
|
|
if m != "0" && m != "1" {
|
|
return nil, ErrSetMuteInput
|
|
}
|
|
|
|
d := setMuteEnvelope{
|
|
XMLName: xml.Name{},
|
|
Schema: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Encoding: "http://schemas.xmlsoap.org/soap/encoding/",
|
|
SetMuteBody: setMuteBody{
|
|
XMLName: xml.Name{},
|
|
SetMuteAction: setMuteAction{
|
|
XMLName: xml.Name{},
|
|
RenderingControl: "urn:schemas-upnp-org:service:RenderingControl:1",
|
|
InstanceID: "0",
|
|
Channel: "Master",
|
|
DesiredMute: m,
|
|
},
|
|
},
|
|
}
|
|
xmlStart := []byte(`<?xml version="1.0" encoding="utf-8"?>`)
|
|
b, err := xml.Marshal(d)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("setMuteSoapBuild Marshal error: %w", err)
|
|
}
|
|
|
|
return append(xmlStart, b...), nil
|
|
}
|
|
|
|
func getMuteSoapBuild() ([]byte, error) {
|
|
d := getMuteEnvelope{
|
|
XMLName: xml.Name{},
|
|
Schema: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Encoding: "http://schemas.xmlsoap.org/soap/encoding/",
|
|
GetMuteBody: getMuteBody{
|
|
XMLName: xml.Name{},
|
|
GetMuteAction: getMuteAction{
|
|
XMLName: xml.Name{},
|
|
RenderingControl: "urn:schemas-upnp-org:service:RenderingControl:1",
|
|
InstanceID: "0",
|
|
Channel: "Master",
|
|
},
|
|
},
|
|
}
|
|
xmlStart := []byte(`<?xml version="1.0" encoding="utf-8"?>`)
|
|
b, err := xml.Marshal(d)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("getMuteSoapBuild Marshal error: %w", err)
|
|
}
|
|
|
|
return append(xmlStart, b...), nil
|
|
}
|
|
|
|
func getVolumeSoapBuild() ([]byte, error) {
|
|
d := getVolumeEnvelope{
|
|
XMLName: xml.Name{},
|
|
Schema: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Encoding: "http://schemas.xmlsoap.org/soap/encoding/",
|
|
GetVolumeBody: getVolumeBody{
|
|
XMLName: xml.Name{},
|
|
GetVolumeAction: getVolumeAction{
|
|
XMLName: xml.Name{},
|
|
RenderingControl: "urn:schemas-upnp-org:service:RenderingControl:1",
|
|
InstanceID: "0",
|
|
Channel: "Master",
|
|
},
|
|
},
|
|
}
|
|
xmlStart := []byte(`<?xml version="1.0" encoding="utf-8"?>`)
|
|
b, err := xml.Marshal(d)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("getVolumeSoapBuild Marshal error: %w", err)
|
|
}
|
|
|
|
return append(xmlStart, b...), nil
|
|
}
|
|
|
|
func setVolumeSoapBuild(v string) ([]byte, error) {
|
|
d := setVolumeEnvelope{
|
|
XMLName: xml.Name{},
|
|
Schema: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Encoding: "http://schemas.xmlsoap.org/soap/encoding/",
|
|
SetVolumeBody: setVolumeBody{
|
|
XMLName: xml.Name{},
|
|
SetVolumeAction: setVolumeAction{
|
|
XMLName: xml.Name{},
|
|
RenderingControl: "urn:schemas-upnp-org:service:RenderingControl:1",
|
|
InstanceID: "0",
|
|
Channel: "Master",
|
|
DesiredVolume: v,
|
|
},
|
|
},
|
|
}
|
|
xmlStart := []byte(`<?xml version="1.0" encoding="utf-8"?>`)
|
|
b, err := xml.Marshal(d)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("setVolumeSoapBuild Marshal error: %w", err)
|
|
}
|
|
|
|
return append(xmlStart, b...), nil
|
|
}
|
|
|
|
func getProtocolInfoSoapBuild() ([]byte, error) {
|
|
d := getProtocolInfoEnvelope{
|
|
XMLName: xml.Name{},
|
|
Schema: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Encoding: "http://schemas.xmlsoap.org/soap/encoding/",
|
|
GetProtocolInfoBody: getProtocolInfoBody{
|
|
XMLName: xml.Name{},
|
|
GetProtocolInfoAction: getProtocolInfoAction{
|
|
XMLName: xml.Name{},
|
|
ConnectionManager: "urn:schemas-upnp-org:service:ConnectionManager:1",
|
|
},
|
|
},
|
|
}
|
|
xmlStart := []byte(`<?xml version="1.0" encoding="utf-8"?>`)
|
|
b, err := xml.Marshal(d)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("getProtocolInfoSoapBuild Marshal error: %w", err)
|
|
}
|
|
|
|
return append(xmlStart, b...), nil
|
|
}
|
|
|
|
func getMediaInfoSoapBuild() ([]byte, error) {
|
|
d := getMediaInfoEnvelope{
|
|
XMLName: xml.Name{},
|
|
Schema: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Encoding: "http://schemas.xmlsoap.org/soap/encoding/",
|
|
GetMediaInfoBody: getMediaInfoBody{
|
|
XMLName: xml.Name{},
|
|
GetMediaInfoAction: getMediaInfoAction{
|
|
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("getMediaInfoSoapBuild Marshal error: %w", err)
|
|
}
|
|
|
|
return append(xmlStart, b...), nil
|
|
}
|
|
|
|
func getTransportInfoSoapBuild() ([]byte, error) {
|
|
d := getTransportInfoEnvelope{
|
|
XMLName: xml.Name{},
|
|
Schema: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Encoding: "http://schemas.xmlsoap.org/soap/encoding/",
|
|
GetTransportInfoBody: getTransportInfoBody{
|
|
XMLName: xml.Name{},
|
|
GetTransportInfoAction: getTransportInfoAction{
|
|
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("getTransportInfoSoapBuild Marshal error: %w", err)
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func seekSoapBuild(reltime string) ([]byte, error) {
|
|
d := seekEnvelope{
|
|
XMLName: xml.Name{},
|
|
Schema: "http://schemas.xmlsoap.org/soap/envelope/",
|
|
Encoding: "http://schemas.xmlsoap.org/soap/encoding/",
|
|
SeekBody: seekBody{
|
|
XMLName: xml.Name{},
|
|
SeekAction: seekAction{
|
|
XMLName: xml.Name{},
|
|
AVTransport: "urn:schemas-upnp-org:service:AVTransport:1",
|
|
InstanceID: "0",
|
|
Unit: "REL_TIME",
|
|
Target: reltime,
|
|
},
|
|
},
|
|
}
|
|
xmlStart := []byte(`<?xml version="1.0" encoding="utf-8"?>`)
|
|
b, err := xml.Marshal(d)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("seekSoapBuild Marshal error: %w", err)
|
|
}
|
|
|
|
return append(xmlStart, b...), nil
|
|
}
|