153 lines
3.8 KiB
Go
153 lines
3.8 KiB
Go
package ui
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/Ullaakut/cameradar/v6"
|
|
)
|
|
|
|
// FormatSummary builds a human-readable summary of discovered streams.
|
|
func FormatSummary(streams []cameradar.Stream, _ error) string {
|
|
accessible, others := partitionStreams(streams)
|
|
|
|
var builder strings.Builder
|
|
builder.WriteString(fmt.Sprintf("Accessible streams: %d\n", len(accessible)))
|
|
if len(accessible) == 0 {
|
|
builder.WriteString("• None\n")
|
|
} else {
|
|
for _, stream := range accessible {
|
|
builder.WriteString(formatStream(stream))
|
|
}
|
|
}
|
|
|
|
if len(others) > 0 {
|
|
builder.WriteString("\n")
|
|
builder.WriteString(fmt.Sprintf("Other discovered streams: %d\n", len(others)))
|
|
for _, stream := range others {
|
|
builder.WriteString(formatStream(stream))
|
|
}
|
|
}
|
|
|
|
return builder.String()
|
|
}
|
|
|
|
func partitionStreams(streams []cameradar.Stream) ([]cameradar.Stream, []cameradar.Stream) {
|
|
var accessible []cameradar.Stream
|
|
var others []cameradar.Stream
|
|
for _, stream := range streams {
|
|
if stream.Available {
|
|
accessible = append(accessible, stream)
|
|
} else {
|
|
others = append(others, stream)
|
|
}
|
|
}
|
|
|
|
// Sort streams by address and port.
|
|
sort.Slice(accessible, func(i, j int) bool {
|
|
if accessible[i].Address.String() == accessible[j].Address.String() {
|
|
return accessible[i].Port < accessible[j].Port
|
|
}
|
|
return accessible[i].Address.String() < accessible[j].Address.String()
|
|
})
|
|
sort.Slice(others, func(i, j int) bool {
|
|
if others[i].Address.String() == others[j].Address.String() {
|
|
return others[i].Port < others[j].Port
|
|
}
|
|
return others[i].Address.String() < others[j].Address.String()
|
|
})
|
|
|
|
return accessible, others
|
|
}
|
|
|
|
func formatStream(stream cameradar.Stream) string {
|
|
var builder strings.Builder
|
|
builder.WriteString("• ")
|
|
builder.WriteString(stream.Address.String())
|
|
builder.WriteString(":")
|
|
builder.WriteString(strconv.FormatUint(uint64(stream.Port), 10))
|
|
|
|
if stream.Device != "" {
|
|
builder.WriteString(" (")
|
|
builder.WriteString(stream.Device)
|
|
builder.WriteString(")")
|
|
}
|
|
builder.WriteString("\n")
|
|
|
|
builder.WriteString(" Authentication: ")
|
|
builder.WriteString(authTypeLabel(stream.AuthenticationType))
|
|
builder.WriteString("\n")
|
|
|
|
if len(stream.Routes) > 0 {
|
|
builder.WriteString(" Routes: ")
|
|
builder.WriteString(strings.Join(stream.Routes, ", "))
|
|
builder.WriteString("\n")
|
|
} else {
|
|
builder.WriteString(" Routes: not found\n")
|
|
}
|
|
|
|
if stream.CredentialsFound {
|
|
builder.WriteString(" Credentials: ")
|
|
builder.WriteString(stream.Username)
|
|
builder.WriteString(":")
|
|
builder.WriteString(stream.Password)
|
|
builder.WriteString("\n")
|
|
} else {
|
|
builder.WriteString(" Credentials: not found\n")
|
|
}
|
|
|
|
builder.WriteString(" Availability: ")
|
|
if stream.Available {
|
|
builder.WriteString("yes\n")
|
|
} else {
|
|
builder.WriteString("no\n")
|
|
}
|
|
|
|
if stream.RouteFound && stream.CredentialsFound {
|
|
builder.WriteString(" RTSP URL: ")
|
|
builder.WriteString(formatRTSPURL(stream))
|
|
builder.WriteString("\n")
|
|
}
|
|
|
|
builder.WriteString(" Admin panel: ")
|
|
builder.WriteString(formatAdminPanelURL(stream))
|
|
builder.WriteString("\n")
|
|
|
|
return builder.String()
|
|
}
|
|
|
|
func formatRTSPURL(stream cameradar.Stream) string {
|
|
path := "/" + strings.TrimLeft(strings.TrimSpace(stream.Route()), "/")
|
|
|
|
credentials := ""
|
|
if stream.Username != "" || stream.Password != "" {
|
|
credentials = stream.Username + ":" + stream.Password + "@"
|
|
}
|
|
|
|
scheme := "rtsp"
|
|
if stream.Secure {
|
|
scheme = "rtsps"
|
|
}
|
|
|
|
return fmt.Sprintf("%s://%s%s:%d%s", scheme, credentials, stream.Address.String(), stream.Port, path)
|
|
}
|
|
|
|
func formatAdminPanelURL(stream cameradar.Stream) string {
|
|
return fmt.Sprintf("http://%s/", stream.Address.String())
|
|
}
|
|
|
|
func authTypeLabel(auth cameradar.AuthType) string {
|
|
switch auth {
|
|
case cameradar.AuthNone:
|
|
return "none"
|
|
case cameradar.AuthBasic:
|
|
return "basic"
|
|
case cameradar.AuthDigest:
|
|
return "digest"
|
|
default:
|
|
return fmt.Sprintf("unknown(%d)", auth)
|
|
}
|
|
}
|