Skip to content

Fix anipub decoy playlists (mpv never starts playback) - #114

Merged
Wraient merged 1 commit into
mainfrom
fix/anipub-decoy-playlist
Aug 2, 2026
Merged

Fix anipub decoy playlists (mpv never starts playback)#114
Wraient merged 1 commit into
mainfrom
fix/anipub-decoy-playlist

Conversation

@Wraient

@Wraient Wraient commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Fixes #113

Problem

anipub/megaplay streams occasionally resolve to ad-injected "decoy" HLS playlists on megap.kotocdn.site. The media playlist's segments point at p16-ad-sg.ibyteimg.com (ByteDance ad CDN) and are 1x1 PNGs. mpv can't decode them, so it stays on the idle "Drop files or URLs to play here." screen and playback never starts.

Confirmed live: A Silent Voice resolved to a playlist where 1770/1770 segments were decoy; Solo Leveling had 12 real / 306 decoy. The decoy playlists are Cloudflare-cached, so this isn't transient.

Previously the stream URL resolved without error, so the provider fallback never engaged.

Fix

Add validateResolvedStream in the anipub provider (internal/providers/anipub/validate.go), called from getEpisodeStreamsForMode:

  • Fetch the master playlist → select highest-bandwidth variant → fetch the media playlist.
  • Reject the stream when all segments are decoy (ibyteimg/ad-site-i18n markers) or decoys make up ≥50% of the playlist.
  • Probe the first real segment's magic bytes (Range request) to catch real segments that 302-redirect to image/HTML content.
  • Skip validation for non-megaplay CDNs and tolerate probe failures so playable streams are never false-rejected.

The validation error propagates through ResolveEpisodeURLForPlayback, so the provider stack falls back to anineko/allanime/etc. instead of opening an idle mpv window.

Testing

  • 22 new unit subtests covering: clean stream passes, fully/majority decoy rejected, redirect-to-decoy rejected, probe-failure tolerated, non-200/non-HLS/empty playlists rejected, non-kotocdn skip.
  • go build ./..., go vet ./..., go test ./internal/... all green.
  • Gated live run (CURD_LIVE_ANIPUB=1) confirmed the real A Silent Voice decoy playlist is rejected (~0.5s).

megap.kotocdn.site master playlists can be served with segments pointing at
p16-ad-sg.ibyteimg.com (ByteDance ad CDN). The segments are 1x1 PNGs, so mpv
stays on its idle screen and never starts playback (fixes #113).

Validate resolved anipub streams before returning them: fetch the media
playlist, reject all-ad or >=50% decoy playlists, and probe the first real
segment's magic bytes for non-video content. Errors propagate up through
ResolveEpisodeURLForPlayback so the provider stack falls back to anineko/
allanime/etc. instead of opening an idle mpv window.
@Wraient
Wraient merged commit 3efccce into main Aug 2, 2026
4 checks passed
@Wraient
Wraient deleted the fix/anipub-decoy-playlist branch August 2, 2026 20:48

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a6cb01715b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return fmt.Errorf("anipub stream media playlist fetch failed: %w", err)
}

segments := parsePlaylistSegments(mediaBody)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject non-HLS media playlists

When a master playlist points to a variant that returns a 200 HTML/challenge page, this code never verifies that mediaBody is an HLS playlist before calling parsePlaylistSegments. The HTML line is treated as a segment; fetchRange then receives an invalid URL and deliberately tolerates the probe failure, so validateResolvedStream returns nil and mpv still opens the bad stream instead of falling back. Please validate the fetched media playlist before segment parsing.

Useful? React with 👍 / 👎.

if isDecoySegmentURI(segment) {
continue
}
if data := v.fetchRange(segment, 0, 15); looksLikeDecoySegment(data) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve relative segment URLs before probing

For media playlists whose segment lines are relative, such as seg/1.ts or /seg/1.ts, this call passes the raw relative URI into http.NewRequest inside fetchRange. That parse error is swallowed as a tolerated probe failure, so a relative segment that redirects to the PNG/HTML decoy is never detected and the AniPub provider is accepted instead of falling back. Resolve the segment against mediaURL before checking/probing it.

Useful? React with 👍 / 👎.

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.

mpv not opening anime

1 participant