bugfix-12
All checks were successful
release-tag / release-image (push) Successful in 1m54s

This commit is contained in:
2025-09-21 22:25:28 +02:00
parent 949f07d420
commit 3addc882d5

View File

@@ -87,6 +87,63 @@ func eq(a, b []byte) bool {
return true
}
type hlsMuxersListResp struct {
Items []struct {
Name string `json:"name"`
Clients []interface{} `json:"clients"` // echte HLS-Viewer (HTTP-Clients)
} `json:"items"`
// manche Builds liefern "items" flach; das fangen wir ab:
Name string `json:"name"`
Clients []interface{} `json:"clients"`
}
func fetchHLSClientMap(ctx context.Context, base, user, pass string) (map[string]int, error) {
u := strings.TrimRight(base, "/") + "/v3/hlsmuxers/list"
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
if err != nil {
return nil, err
}
if user != "" || pass != "" {
req.SetBasicAuth(user, pass)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// Wenn noch kein einziger HLS-Client existiert, liefert MediaMTX oft 200 mit leerer Liste
// (oder 200 mit items=[]). 404 sollte hier nicht auftreten falls doch, behandeln wir es als "leer".
if resp.StatusCode == http.StatusNotFound {
return map[string]int{}, nil
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("hlsmuxers/list: %s", resp.Status)
}
var out hlsMuxersListResp
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
return nil, err
}
m := make(map[string]int)
if len(out.Items) > 0 {
for _, it := range out.Items {
m[it.Name] = len(it.Clients)
}
return m, nil
}
// Fallback: flache Struktur (ein einzelner Muxer)
if out.Name != "" {
m[out.Name] = len(out.Clients)
}
return m, nil
}
type mtxHLSPayload struct {
// v1.15.0 liefert einen Muxer mit Clients-Liste (Feldname kann "clients" heißen)
Item struct {
@@ -146,48 +203,6 @@ func mustLoadTemplates() *template.Template {
return t
}
type mtxPathGetResp struct {
Item struct {
Name string `json:"name"`
Readers []interface{} `json:"readers"`
} `json:"item"`
// einige Builds liefern die Felder auch „flach“:
Name string `json:"name"`
Readers []interface{} `json:"readers"`
}
func fetchMTXViewers(ctx context.Context, base, user, pass, name string) (int, error) {
u := strings.TrimRight(base, "/") + "/v3/paths/get/" + url.PathEscape(name)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
if err != nil {
return 0, err
}
if user != "" || pass != "" {
req.SetBasicAuth(user, pass)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return 0, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return 0, fmt.Errorf("mtx get %s: %s", name, resp.Status)
}
var out mtxPathGetResp
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
return 0, err
}
// Priorität: verschachtelt unter item.*, sonst flach
if out.Item.Readers != nil {
return len(out.Item.Readers), nil
}
return len(out.Readers), nil
}
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
tpls = mustLoadTemplates()
@@ -304,17 +319,30 @@ func main() {
if len(allowed) > 0 && !allowed[p.Name] {
continue
}
// echte HLS-Viewerzahlen einmalig holen (Batch)
hlsMap := map[string]int{}
{
ctxH, cancelH := context.WithTimeout(context.Background(), 2*time.Second)
tmp, err := fetchHLSClientMap(ctxH, mtxAPI, os.Getenv("MTX_API_USER"), os.Getenv("MTX_API_PASS"))
cancelH()
if err == nil && tmp != nil {
hlsMap = tmp
} else {
// optional: log.Printf("warn: hlsmuxers/list: %v", err)
}
}
viewers := 0
// 1) Versuche echte HLS-Clients zu zählen
ctx2, cancel2 := context.WithTimeout(context.Background(), 2*time.Second)
if v, err := hlsViewers(ctx2, mtxAPI, os.Getenv("MTX_API_USER"), os.Getenv("MTX_API_PASS"), p.Name); err == nil {
// 1) Echte HLS-Zuschauer aus der Batch-Map
if v, ok := hlsMap[p.Name]; ok {
viewers = v
}
cancel2()
// 2) Fallback: wenn HLS-API nicht greift (z.B. stream per RTSP/WebRTC gelesen),
// nimm Pfad-Reader (kann >0 bei Nicht-HLS sein, bei HLS meist 1 = Muxer)
// 2) Fallback: Nicht-HLS-Reader (oder wenn Map leer ist)
// Bei HLS ist p.Viewers() meist 1 (der Muxer), aber das wollen wir nur nutzen,
// wenn wir keinen HLS-Wert haben.
if viewers == 0 {
if v := p.Viewers(); v > 0 {
viewers = v
@@ -435,17 +463,29 @@ func apiStreams(w http.ResponseWriter, r *http.Request) {
if len(allowed) > 0 && !allowed[p.Name] {
continue
}
// echte HLS-Viewerzahlen einmalig holen (Batch)
hlsMap := map[string]int{}
{
ctxH, cancelH := context.WithTimeout(context.Background(), 2*time.Second)
tmp, err := fetchHLSClientMap(ctxH, mtxAPI, os.Getenv("MTX_API_USER"), os.Getenv("MTX_API_PASS"))
cancelH()
if err == nil && tmp != nil {
hlsMap = tmp
} else {
// optional: log.Printf("warn: hlsmuxers/list: %v", err)
}
}
viewers := 0
// 1) Versuche echte HLS-Clients zu zählen
ctx2, cancel2 := context.WithTimeout(context.Background(), 2*time.Second)
if v, err := hlsViewers(ctx2, mtxAPI, os.Getenv("MTX_API_USER"), os.Getenv("MTX_API_PASS"), p.Name); err == nil {
// 1) Echte HLS-Zuschauer aus der Batch-Map
if v, ok := hlsMap[p.Name]; ok {
viewers = v
}
cancel2()
// 2) Fallback: wenn HLS-API nicht greift (z.B. stream per RTSP/WebRTC gelesen),
// nimm Pfad-Reader (kann >0 bei Nicht-HLS sein, bei HLS meist 1 = Muxer)
// 2) Fallback: Nicht-HLS-Reader (oder wenn Map leer ist)
// Bei HLS ist p.Viewers() meist 1 (der Muxer), aber das wollen wir nur nutzen,
// wenn wir keinen HLS-Wert haben.
if viewers == 0 {
if v := p.Viewers(); v > 0 {
viewers = v