mirror of
https://github.com/datarhei/core.git
synced 2025-12-24 13:07:56 +08:00
211 lines
3.7 KiB
Go
211 lines
3.7 KiB
Go
package url
|
|
|
|
import (
|
|
"fmt"
|
|
neturl "net/url"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
type URL struct {
|
|
Scheme string
|
|
Host string
|
|
StreamId string
|
|
Options neturl.Values
|
|
}
|
|
|
|
func Parse(srturl string) (*URL, error) {
|
|
u, err := neturl.Parse(srturl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if u.Scheme != "srt" {
|
|
return nil, fmt.Errorf("invalid SRT url")
|
|
}
|
|
|
|
options := u.Query()
|
|
streamid := options.Get("streamid")
|
|
options.Del("streamid")
|
|
|
|
su := &URL{
|
|
Scheme: "srt",
|
|
Host: u.Host,
|
|
StreamId: streamid,
|
|
Options: options,
|
|
}
|
|
|
|
return su, nil
|
|
}
|
|
|
|
func (su *URL) String() string {
|
|
options, _ := neturl.ParseQuery(su.Options.Encode())
|
|
options.Set("streamid", su.StreamId)
|
|
|
|
u := neturl.URL{
|
|
Scheme: su.Scheme,
|
|
Host: su.Host,
|
|
RawQuery: options.Encode(),
|
|
}
|
|
|
|
return u.String()
|
|
}
|
|
|
|
func (su *URL) StreamInfo() (*StreamInfo, error) {
|
|
s, err := ParseStreamId(su.StreamId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &s, nil
|
|
}
|
|
|
|
func (su *URL) SetStreamInfo(si *StreamInfo) {
|
|
su.StreamId = si.String()
|
|
}
|
|
|
|
func (su *URL) Hostname() string {
|
|
u := neturl.URL{
|
|
Host: su.Host,
|
|
}
|
|
|
|
return u.Hostname()
|
|
}
|
|
|
|
func (su *URL) Port() string {
|
|
u := neturl.URL{
|
|
Host: su.Host,
|
|
}
|
|
|
|
return u.Port()
|
|
}
|
|
|
|
type StreamInfo struct {
|
|
Mode string
|
|
Resource string
|
|
Token string
|
|
}
|
|
|
|
func (si *StreamInfo) String() string {
|
|
streamid := si.Resource
|
|
|
|
if len(si.Mode) != 0 && si.Mode != "request" {
|
|
streamid += ",mode:" + si.Mode
|
|
}
|
|
|
|
if len(si.Token) != 0 {
|
|
streamid += ",token:" + si.Token
|
|
}
|
|
|
|
return streamid
|
|
}
|
|
|
|
// ParseStreamId parses a streamid. If the streamid is in the old format
|
|
// it is detected and parsed accordingly. Otherwise the new simplified
|
|
// format will be assumed.
|
|
//
|
|
// resource[,token:{token}]?[,mode:(publish|*request)]?
|
|
//
|
|
// If the mode is not provided, "request" will be assumed.
|
|
func ParseStreamId(streamid string) (StreamInfo, error) {
|
|
si := StreamInfo{Mode: "request"}
|
|
|
|
if decodedStreamid, err := neturl.QueryUnescape(streamid); err == nil {
|
|
streamid = decodedStreamid
|
|
}
|
|
|
|
if strings.HasPrefix(streamid, "#!:") {
|
|
return ParseDeprecatedStreamId(streamid)
|
|
}
|
|
|
|
re := regexp.MustCompile(`,(token|mode):(.+)`)
|
|
|
|
results := map[string]string{}
|
|
|
|
idEnd := -1
|
|
value := streamid
|
|
key := ""
|
|
|
|
for {
|
|
matches := re.FindStringSubmatchIndex(value)
|
|
if matches == nil {
|
|
break
|
|
}
|
|
|
|
if idEnd < 0 {
|
|
idEnd = matches[2] - 1
|
|
}
|
|
|
|
if len(key) != 0 {
|
|
results[key] = value[:matches[2]-1]
|
|
}
|
|
|
|
key = value[matches[2]:matches[3]]
|
|
value = value[matches[4]:matches[5]]
|
|
|
|
results[key] = value
|
|
}
|
|
|
|
if idEnd < 0 {
|
|
idEnd = len(streamid)
|
|
}
|
|
|
|
si.Resource = streamid[:idEnd]
|
|
if token, ok := results["token"]; ok {
|
|
si.Token = token
|
|
}
|
|
|
|
if mode, ok := results["mode"]; ok {
|
|
si.Mode = mode
|
|
} else {
|
|
si.Mode = "request"
|
|
}
|
|
|
|
return si, nil
|
|
}
|
|
|
|
// ParseDeprecatedStreamId parses a streamid in the old format. The old format
|
|
// is based on the recommendation of the SRT specs, but with the special
|
|
// character it contains it can cause some trouble in clients (e.g. kiloview
|
|
// doesn't like the = character).
|
|
func ParseDeprecatedStreamId(streamid string) (StreamInfo, error) {
|
|
si := StreamInfo{Mode: "request"}
|
|
|
|
if !strings.HasPrefix(streamid, "#!:") {
|
|
return si, fmt.Errorf("unknown streamid format")
|
|
}
|
|
|
|
streamid = strings.TrimPrefix(streamid, "#!:")
|
|
|
|
kvs := strings.Split(streamid, ",")
|
|
|
|
split := func(s, sep string) (string, string, error) {
|
|
splitted := strings.SplitN(s, sep, 2)
|
|
|
|
if len(splitted) != 2 {
|
|
return "", "", fmt.Errorf("invalid key/value pair")
|
|
}
|
|
|
|
return splitted[0], splitted[1], nil
|
|
}
|
|
|
|
for _, kv := range kvs {
|
|
key, value, err := split(kv, "=")
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
switch key {
|
|
case "m":
|
|
si.Mode = value
|
|
case "r":
|
|
si.Resource = value
|
|
case "token":
|
|
si.Token = value
|
|
default:
|
|
}
|
|
}
|
|
|
|
return si, nil
|
|
}
|