Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

107 行
1.9KB

  1. package icecast
  2. import (
  3. "strings"
  4. "sync"
  5. )
  6. type RadioTextOptions struct {
  7. Enabled bool
  8. Prefix string
  9. MaxLen int
  10. OnlyOnChange bool
  11. }
  12. func mapStreamTitleToRadioText(streamTitle string, opts RadioTextOptions) string {
  13. if !opts.Enabled {
  14. return ""
  15. }
  16. maxLen := opts.MaxLen
  17. if maxLen <= 0 || maxLen > 64 {
  18. maxLen = 64
  19. }
  20. title := sanitizeASCII(streamTitle)
  21. if title == "" {
  22. return ""
  23. }
  24. prefixRaw := opts.Prefix
  25. prefixHadTrailingSpace := strings.TrimRight(prefixRaw, " \t\r\n") != prefixRaw
  26. prefix := sanitizeASCII(opts.Prefix)
  27. if prefix != "" && prefixHadTrailingSpace {
  28. prefix += " "
  29. }
  30. rt := title
  31. if prefix != "" {
  32. rt = prefix + title
  33. }
  34. if len(rt) > maxLen {
  35. rt = strings.TrimSpace(rt[:maxLen])
  36. }
  37. return rt
  38. }
  39. func sanitizeASCII(raw string) string {
  40. raw = strings.TrimSpace(raw)
  41. if raw == "" {
  42. return ""
  43. }
  44. var b strings.Builder
  45. b.Grow(len(raw))
  46. prevSpace := true
  47. for _, r := range raw {
  48. switch r {
  49. case '\n', '\r', '\t':
  50. r = ' '
  51. }
  52. if r < 0x20 || r == 0x7f || r > 0x7e {
  53. continue
  54. }
  55. if r == ' ' {
  56. if prevSpace {
  57. continue
  58. }
  59. prevSpace = true
  60. b.WriteByte(' ')
  61. continue
  62. }
  63. prevSpace = false
  64. b.WriteByte(byte(r))
  65. }
  66. return strings.TrimSpace(b.String())
  67. }
  68. type RadioTextRelay struct {
  69. opts RadioTextOptions
  70. apply func(string) error
  71. mu sync.Mutex
  72. lastRT string
  73. }
  74. func NewRadioTextRelay(opts RadioTextOptions, initialRT string, apply func(string) error) *RadioTextRelay {
  75. return &RadioTextRelay{
  76. opts: opts,
  77. apply: apply,
  78. lastRT: sanitizeASCII(initialRT),
  79. }
  80. }
  81. func (r *RadioTextRelay) HandleStreamTitle(streamTitle string) error {
  82. if r == nil || r.apply == nil {
  83. return nil
  84. }
  85. next := mapStreamTitleToRadioText(streamTitle, r.opts)
  86. if next == "" {
  87. return nil
  88. }
  89. r.mu.Lock()
  90. skip := r.opts.OnlyOnChange && next == r.lastRT
  91. if !skip {
  92. r.lastRT = next
  93. }
  94. r.mu.Unlock()
  95. if skip {
  96. return nil
  97. }
  98. return r.apply(next)
  99. }