Skip to content

Handle alternate enclosures in all possible paths#4688

Open
leandroalonso wants to merge 6 commits into
trunkfrom
poc-709/support-sync-correctly
Open

Handle alternate enclosures in all possible paths#4688
leandroalonso wants to merge 6 commits into
trunkfrom
poc-709/support-sync-correctly

Conversation

@leandroalonso

@leandroalonso leandroalonso commented Jul 6, 2026

Copy link
Copy Markdown
Member

Fixes POC-709

Populates an episode's hlsUrl on every server → DB path that carries it, so episodes can stream HLS without having to wait for a full podcast refresh. Previously only the feed-refresh (full podcast) path set it, leaving several common paths — most importantly the incremental refresh that delivers new episodes — with no HLS url.

Changes

  • Regenerated api.pb.swift from sync-api. New definitions include Api_AlternateEnclosure (type + sources[].uri, on EpisodeResponse and UpNextResponse.EpisodeResponse), a stats heatmap (Api_StatsHeatmapRequest/Response, Api_HeatmapDay), and an upNextSortTooltipSeen setting.
  • Incremental refresh (/user/update)RefreshEpisode now decodes alternate_enclosures and Episode.populate(fromEpisode:) sets hlsUrl. This is the highest-traffic path (new episodes for subscribed podcasts, delivered on foreground / background / push / pull-to-refresh). Pairs with refresh-server #596 (merged), which adds the field to that response.
  • Up Next sync (protobuf) — a synced episode gets its hlsUrl from alternateEnclosures whether it's already local (UpNextSyncTask) or fetched fresh (threaded through UpNextItemaddToDatabase).
  • Episode update branchServerPodcastManager.addEpisode now carries hlsUrl when refreshing an existing episode (previously omitted).
  • Api_AlternateEnclosure+HLS.swift / RefreshEpisode.hlsUrl — helpers that mirror Episode.hlsUrl(fromEpisodeJson:) (match application/x-mpegURL, case-insensitive, first source URI).
  • Made Episode.hlsEnclosureType public so the Server module can reuse it.

Notes

  • hlsUrl writes are not gated behind FeatureFlag.hls — like the existing feed-refresh path, we always store the url; the flag only gates whether it's used for streaming (EpisodeManager.isStreamingHLS).
  • Not consumed: the full Api_EpisodeResponse.alternate_enclosures (field 21) still isn't read anywhere in the app, so any future protobuf episode endpoint would need wiring — but every path that exists today is now covered.

To test

  1. Delete the app from your simulator
  2. Open https://pocketcasts.com
  3. Login into your test account
  4. Play https://pca.st/episode/ef3e31b2-e52c-4aa9-8423-1bbe493fd90d
  5. Run the app on the simulator
  6. Login with this test account
  7. Play
  8. ✅ Ensure playback is video

Checklist

  • I have considered if this change warrants user-facing release notes and have added them to CHANGELOG.md if necessary.
  • I have considered adding unit tests for my changes.
  • I have updated (or requested that someone edit) the Event Horizon schema to reflect any new or changed analytics.

@dangermattic

dangermattic commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator
1 Warning
⚠️ This PR is larger than 500 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.

Generated by 🚫 Danger

@leandroalonso leandroalonso changed the title Wire HLS alternate enclosures through Up Next sync Handle alternate enclosures in all possible paths Jul 7, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR ensures an episode’s hlsUrl is populated across all relevant server → DB ingestion paths (incremental refresh, Up Next sync, and episode-update flows), enabling HLS video playback without waiting for a full feed refresh.

Changes:

  • Decode/store HLS URLs from refresh-server JSON (alternate_enclosures) and protobuf sync (Api_AlternateEnclosure), and thread them through Up Next syncing and DB insertion.
  • Update server-side episode refresh/update logic to carry hlsUrl into existing episodes.
  • Add unit tests for HLS decoding (JSON + protobuf) and for hlsUrl persistence.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
Modules/Tests/PocketCastsServerTests/RefreshEpisodeHLSTests.swift Adds tests for decoding refresh-server alternate_enclosures into RefreshEpisode.hlsUrl and Episode.populate(fromEpisode:).
Modules/Tests/PocketCastsServerTests/AlternateEnclosureHLSTests.swift Adds tests for extracting HLS URLs from protobuf Api_AlternateEnclosure arrays.
Modules/Tests/PocketCastsDataModelTests/EpisodeHLSUrlPersistenceTests.swift Verifies hlsUrl persists and can be updated via DB save/load.
Modules/Sources/PocketCastsServer/Public/Sync/UpNextSyncTask.swift Populates hlsUrl during Up Next sync for both existing local episodes and server-fetched ones.
Modules/Sources/PocketCastsServer/Public/Sync/Api_AlternateEnclosure+HLS.swift Adds helper to extract HLS URL from protobuf alternate enclosures.
Modules/Sources/PocketCastsServer/Public/Sharing/Structs/UpNextItem.swift Extends UpNextItem to carry an optional hlsUrl through the add-to-DB flow.
Modules/Sources/PocketCastsServer/Public/Sharing/Structs/ServerStructs.swift Decodes refresh-server alternate_enclosures and exposes RefreshEpisode.hlsUrl.
Modules/Sources/PocketCastsServer/Public/ServerPodcastManager.swift Persists hlsUrl when adding episodes from Up Next and when updating an existing episode.
Modules/Sources/PocketCastsServer/Private/Protobuffer/api.pb.swift Regenerates SwiftProtobuf models to include alternate enclosures and other new API fields.
Modules/Sources/PocketCastsServer/Private/Episode+Populate.swift Sets Episode.hlsUrl when populating from RefreshEpisode (incremental refresh path).
Modules/Sources/PocketCastsDataModel/Public/Model/Episode+fromDictionary.swift Makes Episode.hlsEnclosureType public so the Server module can reuse the canonical MIME type.

Comment thread Modules/Sources/PocketCastsServer/Public/ServerPodcastManager.swift Outdated
leandroalonso and others added 2 commits July 7, 2026 11:00
The protobuf Source.uri defaults to "" and a refresh payload could carry an empty
uri; the app treats an empty hlsUrl as "missing" (isStreamingHLS), so normalise
"" to nil at the source rather than persisting a bogus present-but-empty value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The server can omit alternate enclosures on an episode-update response, so only
overwrite hlsUrl when a non-empty value is present — otherwise we'd wipe a url a
feed refresh already stored and regress HLS playback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@leandroalonso

Copy link
Copy Markdown
Member Author

Review feedback addressed

  • ServerPodcastManager.swift:248 (@copilot): episode-update branch no longer clears hlsUrl — only overwrites when a non-empty value is present. Done in 33f4b21
  • Api_AlternateEnclosure+HLS.swift:10 (@copilot): normalise the protobuf "" default to nil at the source. Done in d803209
  • ServerStructs.swift:149 (@copilot): normalise an empty refresh uri to nil. Done in d803209

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.

@leandroalonso leandroalonso marked this pull request as ready for review July 7, 2026 14:23
@leandroalonso leandroalonso requested a review from a team as a code owner July 7, 2026 14:23
@leandroalonso leandroalonso requested review from SergioEstevao and removed request for a team July 7, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants