fix(s3): fix path prefix not applied during segment cleanup (#4799)

* fix(s3): scope segment cleanup to path prefix, fix slice panic, batch deletes

When a path prefix is configured for S3 storage, uploads correctly
write to <prefix>/hls/... but cleanup lists objects from the bucket
root without the prefix. This causes a panic when the resulting
segment list is empty, due to unsafe slicing with a negative index.

- Centralize prefix construction in remoteHLSPrefix() so Save(),
  retrieveAllVideoSegments(), and rewritePlaylistLocations() all
  share the same logic. remoteHLSListingPrefix() adds a trailing
  slash for directory-scoped S3 listing.
- Use strings.Trim to normalize both leading and trailing slashes
  from the configured path prefix.
- Replace ListObjects with ListObjectsPages to handle buckets with
  more than 1000 segments.
- Batch DeleteObjects calls in chunks of 1000 to respect the S3
  API limit, and log per-object errors from the response.
- Guard getDeletableVideoSegmentsWithOffset against empty result
  sets to prevent the slice bounds panic.

Fixes #4784

* fix(s3): handle empty hlsPrefix in rewritePlaylistLocations

LocalStorage calls rewritePlaylistLocations with an empty prefix.
The previous refactor removed the fallback to "/hls", causing
double slashes and missing path segments in playlist URLs.

Default hlsPrefix to "hls" when empty to preserve existing behavior
for local storage setups with a custom video serving endpoint.
This commit is contained in:
Raymon Mens
2026-02-23 14:46:43 -08:00
committed by GitHub
parent 842e5090bd
commit 49ec2a5c2e
2 changed files with 67 additions and 49 deletions
@@ -13,7 +13,7 @@ import (
)
// rewritePlaylistLocations will take a local playlist and rewrite it to have absolute URLs to a specified location.
func rewritePlaylistLocations(localFilePath, remoteServingEndpoint, pathPrefix string) error {
func rewritePlaylistLocations(localFilePath, remoteServingEndpoint, hlsPrefix string) error {
f, err := os.Open(localFilePath) // nolint
if err != nil {
log.Fatalln(err)
@@ -24,15 +24,12 @@ func rewritePlaylistLocations(localFilePath, remoteServingEndpoint, pathPrefix s
log.Warnln(err)
}
if hlsPrefix == "" {
hlsPrefix = "hls"
}
for _, item := range p.Variants {
// Determine the final path to this playlist.
var finalPath string
if pathPrefix != "" {
finalPath = filepath.Join(pathPrefix, "/hls")
} else {
finalPath = "/hls"
}
item.URI = remoteServingEndpoint + filepath.Join(finalPath, item.URI)
item.URI = remoteServingEndpoint + "/" + hlsPrefix + "/" + item.URI
}
publicPath := filepath.Join(config.HLSStoragePath, filepath.Base(localFilePath))