Merge pull request #5409 from mikiher/podcast-scanner-stale-episodes
Some checks are pending
CodeQL / Analyze (push) Waiting to run
Build and Push Docker Image / build (push) Waiting to run
Integration Test / build and test (push) Waiting to run
Run Unit Tests / Run Unit Tests (push) Waiting to run

Fix podcast rescan emitting stale episodes on item_updated
This commit is contained in:
advplyr 2026-07-28 18:15:19 -04:00 committed by GitHub
commit 6caa1aa2da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -58,8 +58,12 @@ class PodcastScanner {
/** @type {AudioFile[]} */ /** @type {AudioFile[]} */
let newAudioFiles = [] let newAudioFiles = []
// Track episode add/remove/update so item_updated includes the current episode list
let hasEpisodeChanges = false
if (libraryItemData.hasAudioFileChanges || libraryItemData.audioLibraryFiles.length !== existingPodcastEpisodes.length) { if (libraryItemData.hasAudioFileChanges || libraryItemData.audioLibraryFiles.length !== existingPodcastEpisodes.length) {
// Filter out and destroy episodes that were removed // Filter out and destroy episodes that were removed.
// filter() returns a new array — reassign to media.podcastEpisodes before emit.
const episodesToRemove = [] const episodesToRemove = []
existingPodcastEpisodes = existingPodcastEpisodes.filter((ep) => { existingPodcastEpisodes = existingPodcastEpisodes.filter((ep) => {
if (libraryItemData.checkAudioFileRemoved(ep.audioFile)) { if (libraryItemData.checkAudioFileRemoved(ep.audioFile)) {
@ -70,6 +74,7 @@ class PodcastScanner {
}) })
if (episodesToRemove.length) { if (episodesToRemove.length) {
hasEpisodeChanges = true
// Remove episodes from playlists and media progress // Remove episodes from playlists and media progress
const episodeIds = episodesToRemove.map((ep) => ep.id) const episodeIds = episodesToRemove.map((ep) => ep.id)
await Database.playlistModel.removeMediaItemsFromPlaylists(episodeIds) await Database.playlistModel.removeMediaItemsFromPlaylists(episodeIds)
@ -116,6 +121,7 @@ class PodcastScanner {
AudioFileScanner.setPodcastEpisodeMetadataFromAudioMetaTags(podcastEpisode, libraryScan) AudioFileScanner.setPodcastEpisodeMetadataFromAudioMetaTags(podcastEpisode, libraryScan)
libraryScan.addLog(LogLevel.INFO, `Podcast episode "${podcastEpisode.title}" keys changed [${podcastEpisode.changed()?.join(', ')}]`) libraryScan.addLog(LogLevel.INFO, `Podcast episode "${podcastEpisode.title}" keys changed [${podcastEpisode.changed()?.join(', ')}]`)
await podcastEpisode.save() await podcastEpisode.save()
hasEpisodeChanges = true
} }
} }
@ -155,9 +161,13 @@ class PodcastScanner {
libraryScan.addLog(LogLevel.INFO, `New Podcast episode "${newPodcastEpisode.title}" added`) libraryScan.addLog(LogLevel.INFO, `New Podcast episode "${newPodcastEpisode.title}" added`)
await newPodcastEpisode.save() await newPodcastEpisode.save()
existingPodcastEpisodes.push(newPodcastEpisode) existingPodcastEpisodes.push(newPodcastEpisode)
hasEpisodeChanges = true
} }
} }
// Keep association in sync for toOldJSONExpanded() / item_updated socket payload
media.podcastEpisodes = existingPodcastEpisodes
let hasMediaChanges = false let hasMediaChanges = false
if (existingPodcastEpisodes.length !== media.numEpisodes) { if (existingPodcastEpisodes.length !== media.numEpisodes) {
media.numEpisodes = existingPodcastEpisodes.length media.numEpisodes = existingPodcastEpisodes.length
@ -250,7 +260,7 @@ class PodcastScanner {
return { return {
libraryItem: existingLibraryItem, libraryItem: existingLibraryItem,
wasUpdated: hasMediaChanges || libraryItemUpdated wasUpdated: hasMediaChanges || libraryItemUpdated || hasEpisodeChanges
} }
} }