Parcourir la source

ingest: harden adapter metadata and shutdown handling

main
Jan il y a 1 mois
Parent
révision
14bdbbabea
2 fichiers modifiés avec 40 ajouts et 3 suppressions
  1. +39
    -3
      internal/ingest/adapters/icecast/icy.go
  2. +1
    -0
      internal/ingest/adapters/stdinpcm/source.go

+ 39
- 3
internal/ingest/adapters/icecast/icy.go Voir le fichier

@@ -77,18 +77,31 @@ func (r *icyReader) readMetadataBlock() error {
return nil
}

// parseICYMetadata parses the ICY inline metadata block.
//
// ICY metadata is a semicolon-delimited key=value format where values are
// single-quoted strings. A naive strings.Split(raw, ";") breaks when the
// StreamTitle itself contains semicolons (e.g. "Artist - Title; Live Edit").
// This parser is quote-aware: it only splits on semicolons that appear
// outside of single-quoted value strings.
func parseICYMetadata(block []byte) icyMetadata {
raw := strings.TrimRight(string(bytes.Trim(block, "\x00")), "\x00")
meta := icyMetadata{}
for _, field := range strings.Split(raw, ";") {

fields := splitICYFields(raw)
for _, field := range fields {
field = strings.TrimSpace(field)
if !strings.HasPrefix(field, "StreamTitle=") {
continue
}
v := strings.TrimPrefix(field, "StreamTitle=")
v = strings.TrimSpace(v)
if len(v) >= 2 && ((v[0] == '\'' && v[len(v)-1] == '\'') || (v[0] == '"' && v[len(v)-1] == '"')) {
v = v[1 : len(v)-1]
// Strip enclosing single or double quotes.
if len(v) >= 2 {
if (v[0] == '\'' && v[len(v)-1] == '\'') ||
(v[0] == '"' && v[len(v)-1] == '"') {
v = v[1 : len(v)-1]
}
}
meta.StreamTitle = v
break
@@ -96,6 +109,29 @@ func parseICYMetadata(block []byte) icyMetadata {
return meta
}

// splitICYFields splits an ICY metadata string on semicolons that appear
// outside of single-quoted value strings. Semicolons inside quotes (e.g.
// StreamTitle='Artist - Song; Live';) are preserved as part of the value.
func splitICYFields(s string) []string {
var fields []string
inQuote := false
start := 0
for i := 0; i < len(s); i++ {
c := s[i]
if c == '\'' {
inQuote = !inQuote
}
if c == ';' && !inQuote {
fields = append(fields, s[start:i])
start = i + 1
}
}
if start < len(s) {
fields = append(fields, s[start:])
}
return fields
}

func parseICYMetaInt(raw string) (int, error) {
raw = strings.TrimSpace(raw)
if raw == "" {


+ 1
- 0
internal/ingest/adapters/stdinpcm/source.go Voir le fichier

@@ -119,6 +119,7 @@ func (s *Source) Stats() ingest.SourceStats {

func (s *Source) readLoop(ctx context.Context) {
defer s.wg.Done()
defer close(s.errs)
defer close(s.chunks)

frameBytes := s.channels * 2


Chargement…
Annuler
Enregistrer