Skip to content

fix: proxy playlist parsing logic#1205

Merged
Zibbp merged 2 commits into
mainfrom
proxy-playlist-fix
Jun 7, 2026
Merged

fix: proxy playlist parsing logic#1205
Zibbp merged 2 commits into
mainfrom
proxy-playlist-fix

Conversation

@Zibbp

@Zibbp Zibbp commented Jun 7, 2026

Copy link
Copy Markdown
Owner
  1. replace deprecated hls library with a supported one
  2. abstract parsing logic to separate package
  3. fix hls playlist parsing logic for a specific proxy server
    1. This proxy server no longer returns the video quality in the VIDEO attribute

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR replaces grafov/m3u8 with an internal HLS decoder using gohlslib. It adds DecodeMultivariant with Twitch-specific normalization, updates call sites (platform, exec/download, proxy tests) to use *hls.Multivariant, and includes tests plus audio-only archive/live test additions.

Changes

HLS Library Migration

Layer / File(s) Summary
HLS multivariant decoder with Twitch normalization
internal/hls/playlist.go
New DecodeMultivariant, MaxPlaylistSize, and Multivariant alias. Size-limits input, normalizes missing VIDEO attributes on #EXT-X-STREAM-INF using Twitch metadata or codec inspection, and unmarshals via gohlslib.
HLS module tests and dependency update
go.mod, internal/hls/playlist_test.go
Add three tests validating DecodeMultivariant behavior for Twitch playlists and update go.mod to add gohlslib/v2 while removing grafov/m3u8.
Stream fetching migration to internal HLS decoder
internal/platform/twitch.go
TwitchConnection.GetStream now returns *hls.Multivariant and decodes responses via hls.DecodeMultivariant, with updated imports and error handling.
Download and proxy testing migration to internal HLS decoder
internal/exec/exec.go, internal/exec/util.go
Update DownloadTwitchLiveVideo and proxy-test helpers to use *hls.Multivariant and decode playlist responses with hls.DecodeMultivariant; adjust body reading and imports.
Archive tests: audio-only verification
internal/archive/archive_test.go
Add assertAudioOnlyFile ffprobe helper and TestArchiveVideoAudioOnlyNoChat to verify audio-only archives with chat disabled produce audio-only VODs and no chat artifacts.
Live tests: watched channel audio-only scenario
internal/live/live_test.go
Add audio-only live-test helpers and TestTwitchWatchedChannelLiveAudioOnlyNoChat, configuring a watched channel with Resolution: "audio" and chat disabled, validating audio-only archival outcomes.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.46% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: fixing proxy playlist parsing logic by replacing a deprecated HLS library with a supported one and abstracting parsing logic.
Description check ✅ Passed The description is directly related to the changeset, listing the three main objectives: library replacement, logic abstraction, and parsing fix for proxy server responses.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch proxy-playlist-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
internal/exec/util.go (1)

61-69: 💤 Low value

Inconsistent logging and potential information leak.

Using fmt.Println for debug output is inconsistent with the rest of the codebase, which uses the zerolog/log package. Additionally, printing the raw HLS playlist to stdout may expose sensitive information, as playlist variant URIs can contain authentication tokens or signed parameters.

Consider using log.Debug().Msg(string(bodyBytes)) instead, or remove this debug output entirely if it was only needed during development.

♻️ Proposed fix to use structured logging
-	fmt.Println("===")
-	// print body
 	bodyBytes, err := io.ReadAll(resp.Body)
 	if err != nil {
 		log.Error().Err(err).Msg("error reading response body for Twitch HLS proxy server test")
 		return nil, false
 	}
-	fmt.Println(string(bodyBytes))
-	fmt.Println("===")
+	log.Debug().Str("playlist_body", string(bodyBytes)).Msg("Twitch HLS proxy response")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/exec/util.go` around lines 61 - 69, Replace the fmt.Println debug
output that prints the raw HLS playlist (the io.ReadAll(resp.Body) / bodyBytes
printing) with structured zerolog logging or remove it to avoid leaking auth
tokens; specifically, in the code around the io.ReadAll(resp.Body) call and the
subsequent fmt.Println statements, stop writing to stdout and instead call
log.Debug().Msg(string(bodyBytes)) if you need the content for troubleshooting,
or drop the debug output entirely, keeping the existing
log.Error().Err(err).Msg(...) for read failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/exec/util.go`:
- Around line 63-71: Unbounded io.ReadAll on resp.Body can exhaust memory before
hls.DecodeMultivariant's internal limit runs; wrap resp.Body with io.LimitReader
(using the same maxPlaylistSize constant used by internal/hls/playlist.go) and
read from that limited reader when producing bodyBytes and when passing to
hls.DecodeMultivariant so both the debug output and DecodeMultivariant enforce
the same size cap (adjust references in internal/exec/util.go where bodyBytes,
resp.Body and hls.DecodeMultivariant are used).

---

Nitpick comments:
In `@internal/exec/util.go`:
- Around line 61-69: Replace the fmt.Println debug output that prints the raw
HLS playlist (the io.ReadAll(resp.Body) / bodyBytes printing) with structured
zerolog logging or remove it to avoid leaking auth tokens; specifically, in the
code around the io.ReadAll(resp.Body) call and the subsequent fmt.Println
statements, stop writing to stdout and instead call
log.Debug().Msg(string(bodyBytes)) if you need the content for troubleshooting,
or drop the debug output entirely, keeping the existing
log.Error().Err(err).Msg(...) for read failures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f77fa68b-195e-46a6-b871-8662b3492aa4

📥 Commits

Reviewing files that changed from the base of the PR and between 32b8911 and accaae8.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (6)
  • go.mod
  • internal/exec/exec.go
  • internal/exec/util.go
  • internal/hls/playlist.go
  • internal/hls/playlist_test.go
  • internal/platform/twitch.go

Comment thread internal/exec/util.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/archive/archive_test.go (1)

66-86: ⚡ Quick win

Extract duplicated helper to shared test package.

The assertAudioOnlyFile helper is duplicated in internal/live/live_test.go (lines 242-262). Extract this to a shared test helper (e.g., tests/shared/media_helpers.go or similar) to avoid duplication and ensure consistent behavior across test suites.

♻️ Suggested refactor

Create a new file tests/shared/media_helpers.go:

package tests_shared

import (
	"testing"
	
	"github.com/stretchr/testify/assert"
	internalExec "github.com/zibbp/ganymede/internal/exec"
)

func AssertAudioOnlyFile(t *testing.T, path string) {
	t.Helper()
	
	probeData, err := internalExec.GetFfprobeVideoData(t.Context(), path)
	assert.NoError(t, err, "Failed to probe archived media")
	assert.NotNil(t, probeData, "Expected ffprobe data for archived media")
	
	audioStreams := 0
	videoStreams := 0
	for _, stream := range probeData.Streams {
		switch stream.CodecType {
		case "audio":
			audioStreams++
		case "video":
			videoStreams++
		}
	}
	
	assert.Greater(t, audioStreams, 0, "Archived media should contain at least one audio stream")
	assert.Zero(t, videoStreams, "Archived media should not contain video streams")
}

Then replace both local helpers with calls to tests_shared.AssertAudioOnlyFile(t, path).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/archive/archive_test.go` around lines 66 - 86, The helper
assertAudioOnlyFile is duplicated; extract it into a shared test helper named
AssertAudioOnlyFile in a new tests_shared package, implementing the same logic
(use internalExec.GetFfprobeVideoData, t.Helper(), assert.NoError/NotNil, count
audio/video streams and assert results) and then remove the local
assertAudioOnlyFile definitions and replace calls with
tests_shared.AssertAudioOnlyFile(t, path) in the test suites that currently
duplicate that logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/archive/archive_test.go`:
- Around line 66-86: The helper assertAudioOnlyFile is duplicated; extract it
into a shared test helper named AssertAudioOnlyFile in a new tests_shared
package, implementing the same logic (use internalExec.GetFfprobeVideoData,
t.Helper(), assert.NoError/NotNil, count audio/video streams and assert results)
and then remove the local assertAudioOnlyFile definitions and replace calls with
tests_shared.AssertAudioOnlyFile(t, path) in the test suites that currently
duplicate that logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b8642675-7c74-46f4-981e-ee089f537a62

📥 Commits

Reviewing files that changed from the base of the PR and between accaae8 and 66df1e0.

📒 Files selected for processing (4)
  • internal/archive/archive_test.go
  • internal/exec/util.go
  • internal/hls/playlist.go
  • internal/live/live_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/exec/util.go
  • internal/hls/playlist.go

@Zibbp Zibbp merged commit 7708d14 into main Jun 7, 2026
7 of 8 checks passed
@Zibbp Zibbp deleted the proxy-playlist-fix branch June 7, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant