Update episode thumbnail status when embedded artwork is extracted during playback - #5548
Update episode thumbnail status when embedded artwork is extracted during playback#5548joashrajin wants to merge 4 commits into
Conversation
…ring playback Re-lands #5303 with review feedback addressed: only resolve the thumbnail status when artwork extraction was actually attempted (useEpisodeArtwork enabled), so episodes are not permanently marked EMBEDDED_NOT_AVAILABLE while the setting is off. PCDROID-79
|
Claude finished @joashrajin's task in 3m 2s —— View job Code ReviewI reviewed the change against 🟡 1. TOCTOU on the
|
|
Claude finished @joashrajin's task in 2m 35s —— View job Review 🔍
Nice, focused fix. The root-cause analysis is correct: A couple of things worth considering before merge: 🟠 Full-row read‑modify‑write can clobber concurrent updatesval episode = episodeManager.findEpisodeByUuid(playbackState.episodeUuid) as? PodcastEpisode
if (episode != null && episode.thumbnailStatus == PodcastEpisode.THUMBNAIL_STATUS_UNKNOWN) {
episode.thumbnailStatus = thumbnailStatus
episodeManager.update(episode) // @Update → writes the ENTIRE row
}
@Query("UPDATE podcast_episodes SET thumbnail_status = :thumbnailStatus WHERE uuid = :uuid AND thumbnail_status = ${PodcastEpisode.THUMBNAIL_STATUS_UNKNOWN}")
abstract suspend fun updateThumbnailStatusIfUnknown(thumbnailStatus: Int, uuid: String)Folding the 🟡
|
There was a problem hiding this comment.
Pull request overview
Ensures episode artwork can update on the Filters page after embedded artwork is extracted during playback by persisting an episode-level thumbnailStatus change so Room re-emits and the UI rebinds.
Changes:
- Update
PlaybackManager.onMetadataAvailable()to resolve and persistPodcastEpisode.thumbnailStatus(when stillUNKNOWN) after metadata is read. - Add
resolveThumbnailStatus()helper and unit tests covering the “setting off / extracted / attempted but none found” cases. - Add a changelog entry for the user-visible bug fix.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| modules/services/repositories/src/main/java/au/com/shiftyjelly/pocketcasts/repositories/playback/PlaybackManager.kt | Resolves and persists thumbnailStatus after metadata extraction to trigger Room/UI updates. |
| modules/services/repositories/src/test/java/au/com/shiftyjelly/pocketcasts/repositories/playback/ResolveThumbnailStatusTest.kt | Unit tests for thumbnail status resolution behavior. |
| CHANGELOG.md | Documents the bug fix in release notes. |
| val episode = episodeManager.findEpisodeByUuid(playbackState.episodeUuid) as? PodcastEpisode | ||
| if (episode != null && episode.thumbnailStatus == PodcastEpisode.THUMBNAIL_STATUS_UNKNOWN) { | ||
| episode.thumbnailStatus = thumbnailStatus | ||
| episodeManager.update(episode) | ||
| } |
There was a problem hiding this comment.
Fixed in 245e03c — replaced the full-row update with a targeted conditional query (UPDATE podcast_episodes SET thumbnail_status = :thumbnailStatus WHERE uuid = :uuid AND thumbnail_status = 0) exposed via EpisodeManager.updateThumbnailStatusIfUnknown(). The same commit also addresses the setting-toggle race by capturing artworkExtractionAttempted on EpisodeFileMetadata during read() instead of re-reading the setting.
…extraction flag - Replace the full-row episode update with a targeted conditional EpisodeDao query (only sets thumbnail_status while still UNKNOWN), avoiding clobbering concurrent playback-state column writes. - Capture whether artwork extraction was attempted on EpisodeFileMetadata during read() instead of re-reading the useEpisodeArtwork setting later, closing the toggle race window.
|
Claude finished @joashrajin's task in 1m 14s —— View job Re-review (commit 245e03c) 🔍
Thanks for the quick turnaround — I traced the updated flow end-to-end and both concerns from the earlier reviews are now properly resolved. ✅ Full-row read-modify-write → atomic conditional query
@Query("UPDATE podcast_episodes SET thumbnail_status = :thumbnailStatus WHERE uuid = :uuid AND thumbnail_status = ${PodcastEpisode.THUMBNAIL_STATUS_UNKNOWN}")This eliminates the read entirely and folds the ✅ Setting TOCTOU → single source of truth
🟢 Optional polish (non-blocking)
Summary: The two substantive issues (lost-update race + setting TOCTOU) are both fixed correctly, and the extracted |
|
Claude finished @joashrajin's task in 1m 29s —— View job Review 🔍
I re-reviewed against ✅ Atomic conditional update (lost-update race resolved)
@Query("UPDATE podcast_episodes SET thumbnail_status = :thumbnailStatus WHERE uuid = :uuid AND thumbnail_status = ${PodcastEpisode.THUMBNAIL_STATUS_UNKNOWN}")The read-modify-write is gone and the ✅ Setting TOCTOU resolved
🟢 One correction to the earlier "write-only field" noteEarlier reviews called 🟢 Optional polish (non-blocking)
Summary: Correct, minimal fix for the reported bug; supersedes #5303 cleanly and both prior race concerns remain fixed after the merge. Nothing blocking — the only suggestion is the clarifying comment at the write site. ✅ |
|
Would you mind sharing which podcast you used to test? One with embedded artwork but it isn't in the feed? |
|
Thanks for the ping, @geekygecko I never dig into it too much, but was testing using https://pca.st/upgrade and https://pca.st/moreorless (although this one generally has the same podcast artwork as the episode artwork) I found a reliable fixture and was able to reproduce the intermittent behavior naturally on the pre-fix build. Fixture
Natural reproduction
On the pre-fix build (
This means the failure can be a short stale interval rather than a permanently wrong image. The old build eventually appears correct because the unrelated position save changes the episode row, Room emits it again, and DiffUtil rebinds the artwork. The episode's On the fixed build ( |
|
Version |
Description
Fixes PCDROID-79
Supersedes #5303 by @lromero16 (author is away; this re-lands her fix with the review feedback addressed). Root-cause analysis is hers:
Root cause: When embedded artwork is extracted from a downloaded audio file during playback and saved to the artwork cache file, the episode's DB record was never updated —
PlaybackManager.onMetadataAvailable()never setPodcastEpisode.thumbnailStatus. Because Room didn't re-emit, DiffUtil detected no change and the filters page ViewHolder was never rebound, so the newly extracted artwork never appeared.Fix: After saving chapters in
onMetadataAvailable(), resolve and persistthumbnailStatus(only when stillTHUMBNAIL_STATUS_UNKNOWN). The DB write triggers Room → DiffUtil → rebind → Coil loads the cached artwork file.Change from #5303: the original set
THUMBNAIL_STATUS_EMBEDDED_NOT_AVAILABLEeven when the "use episode artwork" setting is off — but in that caseEpisodeFileMetadata.read()never attempts extraction, so the episode would be permanently marked as having no artwork and never re-evaluated if the user enabled the setting later. The status is now only resolved when extraction was actually attempted (resolveThumbnailStatus()returnsnullotherwise), with unit tests covering all three cases.Testing Instructions
Screenshots or Screencast
Checklist
./gradlew spotlessApplyto automatically apply formatting/linting)ResolveThumbnailStatusTest)modules/services/localization/src/main/res/values/strings.xml— n/aI have tested any UI changes...