fix(stream): EXT-X-START=0 en el playlist copy mientras crece

Hasta que llega ENDLIST la sesión copy es un EVENT creciente y algunos
players nativos (iOS) tratan un playlist sin terminar como LIVE: se
enganchan al borde en vez de a la posición 0. EXT-X-START:TIME-OFFSET=0
(RFC 8216 §4.3.5.2) fija el arranque explícitamente; inofensivo cuando el
playlist ya es final. Coincide con el patrón observado: episodios cortos
(ENDLIST en segundos) reproducían en iPhone, películas (EVENT durante
minutos) no.
This commit is contained in:
Deivid Soto 2026-06-10 23:51:14 +02:00
parent 9eb3e44153
commit 6c756a2569

View file

@ -1212,7 +1212,18 @@ func (s *HLSSession) serveCopyPlaylist(w http.ResponseWriter, r *http.Request) {
for { for {
data, err := os.ReadFile(path) data, err := os.ReadFile(path)
if err == nil && len(data) > 0 { if err == nil && len(data) > 0 {
_, _ = w.Write(data) // Until ENDLIST lands a copy session is a growing EVENT playlist,
// and some native players (iOS) treat any not-yet-ended playlist
// like LIVE and join at the live edge instead of position 0.
// EXT-X-START pins the start to 0 explicitly (RFC 8216 §4.3.5.2);
// harmless once the playlist is final.
out := data
if !strings.Contains(string(data), "#EXT-X-START") {
out = []byte(strings.Replace(string(data),
"#EXT-X-VERSION:7\n",
"#EXT-X-VERSION:7\n#EXT-X-START:TIME-OFFSET=0,PRECISE=YES\n", 1))
}
_, _ = w.Write(out)
return return
} }
if r.Context().Err() != nil || time.Now().After(deadline) { if r.Context().Err() != nil || time.Now().After(deadline) {