mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-11-02 18:37:01 -05:00 
			
		
		
		
	Merge pull request #4041 from nichwall/podcast_queue_no_duplicates
Prevent duplicate episodes from being added to queue
This commit is contained in:
		
						commit
						e4a34b0145
					
				@ -16,11 +16,12 @@
 | 
				
			|||||||
          v-for="(episode, index) in episodesList"
 | 
					          v-for="(episode, index) in episodesList"
 | 
				
			||||||
          :key="index"
 | 
					          :key="index"
 | 
				
			||||||
          class="relative"
 | 
					          class="relative"
 | 
				
			||||||
          :class="getIsEpisodeDownloaded(episode) ? 'bg-primary bg-opacity-40' : selectedEpisodes[episode.cleanUrl] ? 'cursor-pointer bg-success bg-opacity-10' : index % 2 == 0 ? 'cursor-pointer bg-primary bg-opacity-25 hover:bg-opacity-40' : 'cursor-pointer bg-primary bg-opacity-5 hover:bg-opacity-25'"
 | 
					          :class="episode.isDownloaded || episode.isDownloading ? 'bg-primary bg-opacity-40' : selectedEpisodes[episode.cleanUrl] ? 'cursor-pointer bg-success bg-opacity-10' : index % 2 == 0 ? 'cursor-pointer bg-primary bg-opacity-25 hover:bg-opacity-40' : 'cursor-pointer bg-primary bg-opacity-5 hover:bg-opacity-25'"
 | 
				
			||||||
          @click="toggleSelectEpisode(episode)"
 | 
					          @click="toggleSelectEpisode(episode)"
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <div class="absolute top-0 left-0 h-full flex items-center p-2">
 | 
					          <div class="absolute top-0 left-0 h-full flex items-center p-2">
 | 
				
			||||||
            <span v-if="getIsEpisodeDownloaded(episode)" class="material-symbols text-success text-xl">download_done</span>
 | 
					            <span v-if="episode.isDownloaded" class="material-symbols text-success text-xl">download_done</span>
 | 
				
			||||||
 | 
					            <span v-else-if="episode.isDownloading" class="material-symbols text-warning text-xl">download</span>
 | 
				
			||||||
            <ui-checkbox v-else v-model="selectedEpisodes[episode.cleanUrl]" small checkbox-bg="primary" border-color="gray-600" />
 | 
					            <ui-checkbox v-else v-model="selectedEpisodes[episode.cleanUrl]" small checkbox-bg="primary" border-color="gray-600" />
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
          <div class="px-8 py-2">
 | 
					          <div class="px-8 py-2">
 | 
				
			||||||
@ -58,6 +59,14 @@ export default {
 | 
				
			|||||||
    episodes: {
 | 
					    episodes: {
 | 
				
			||||||
      type: Array,
 | 
					      type: Array,
 | 
				
			||||||
      default: () => []
 | 
					      default: () => []
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    downloadQueue: {
 | 
				
			||||||
 | 
					      type: Array,
 | 
				
			||||||
 | 
					      default: () => []
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    episodesDownloading: {
 | 
				
			||||||
 | 
					      type: Array,
 | 
				
			||||||
 | 
					      default: () => []
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  data() {
 | 
					  data() {
 | 
				
			||||||
@ -79,6 +88,21 @@ export default {
 | 
				
			|||||||
      handler(newVal) {
 | 
					      handler(newVal) {
 | 
				
			||||||
        if (newVal) this.init()
 | 
					        if (newVal) this.init()
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    episodes: {
 | 
				
			||||||
 | 
					      handler(newVal) {
 | 
				
			||||||
 | 
					        if (newVal) this.updateEpisodeDownloadStatuses()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    episodesDownloading: {
 | 
				
			||||||
 | 
					      handler(newVal) {
 | 
				
			||||||
 | 
					        if (newVal) this.updateEpisodeDownloadStatuses()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    downloadQueue: {
 | 
				
			||||||
 | 
					      handler(newVal) {
 | 
				
			||||||
 | 
					        if (newVal) this.updateEpisodeDownloadStatuses()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  computed: {
 | 
					  computed: {
 | 
				
			||||||
@ -132,6 +156,13 @@ export default {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      return false
 | 
					      return false
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    getIsEpisodeDownloadingOrQueued(episode) {
 | 
				
			||||||
 | 
					      const episodesToCheck = [...this.episodesDownloading, ...this.downloadQueue]
 | 
				
			||||||
 | 
					      if (episode.guid) {
 | 
				
			||||||
 | 
					        return episodesToCheck.some((download) => download.guid === episode.guid)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return episodesToCheck.some((download) => this.getCleanEpisodeUrl(download.url) === episode.cleanUrl)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * UPDATE: As of v2.4.5 guid is used for matching existing downloaded episodes if it is found on the RSS feed.
 | 
					     * UPDATE: As of v2.4.5 guid is used for matching existing downloaded episodes if it is found on the RSS feed.
 | 
				
			||||||
     * Fallback to checking the clean url
 | 
					     * Fallback to checking the clean url
 | 
				
			||||||
@ -187,7 +218,7 @@ export default {
 | 
				
			|||||||
      this.selectAll = true
 | 
					      this.selectAll = true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    toggleSelectEpisode(episode) {
 | 
					    toggleSelectEpisode(episode) {
 | 
				
			||||||
      if (this.getIsEpisodeDownloaded(episode)) return
 | 
					      if (episode.isDownloaded || episode.isDownloading) return
 | 
				
			||||||
      this.$set(this.selectedEpisodes, episode.cleanUrl, !this.selectedEpisodes[episode.cleanUrl])
 | 
					      this.$set(this.selectedEpisodes, episode.cleanUrl, !this.selectedEpisodes[episode.cleanUrl])
 | 
				
			||||||
      this.checkSetIsSelectedAll()
 | 
					      this.checkSetIsSelectedAll()
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -223,6 +254,23 @@ export default {
 | 
				
			|||||||
        })
 | 
					        })
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    init() {
 | 
					    init() {
 | 
				
			||||||
 | 
					      this.updateDownloadedEpisodeMaps()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.episodesCleaned = this.episodes
 | 
				
			||||||
 | 
					        .filter((ep) => ep.enclosure?.url)
 | 
				
			||||||
 | 
					        .map((_ep) => {
 | 
				
			||||||
 | 
					          return {
 | 
				
			||||||
 | 
					            ..._ep,
 | 
				
			||||||
 | 
					            cleanUrl: this.getCleanEpisodeUrl(_ep.enclosure.url),
 | 
				
			||||||
 | 
					            isDownloading: this.getIsEpisodeDownloadingOrQueued(_ep),
 | 
				
			||||||
 | 
					            isDownloaded: this.getIsEpisodeDownloaded(_ep)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      this.episodesCleaned.sort((a, b) => (a.publishedAt < b.publishedAt ? 1 : -1))
 | 
				
			||||||
 | 
					      this.selectAll = false
 | 
				
			||||||
 | 
					      this.selectedEpisodes = {}
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    updateDownloadedEpisodeMaps() {
 | 
				
			||||||
      this.downloadedEpisodeGuidMap = {}
 | 
					      this.downloadedEpisodeGuidMap = {}
 | 
				
			||||||
      this.downloadedEpisodeUrlMap = {}
 | 
					      this.downloadedEpisodeUrlMap = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -230,18 +278,16 @@ export default {
 | 
				
			|||||||
        if (episode.guid) this.downloadedEpisodeGuidMap[episode.guid] = episode.id
 | 
					        if (episode.guid) this.downloadedEpisodeGuidMap[episode.guid] = episode.id
 | 
				
			||||||
        if (episode.enclosure?.url) this.downloadedEpisodeUrlMap[this.getCleanEpisodeUrl(episode.enclosure.url)] = episode.id
 | 
					        if (episode.enclosure?.url) this.downloadedEpisodeUrlMap[this.getCleanEpisodeUrl(episode.enclosure.url)] = episode.id
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
      this.episodesCleaned = this.episodes
 | 
					    updateEpisodeDownloadStatuses() {
 | 
				
			||||||
        .filter((ep) => ep.enclosure?.url)
 | 
					      this.updateDownloadedEpisodeMaps()
 | 
				
			||||||
        .map((_ep) => {
 | 
					      this.episodesCleaned = this.episodesCleaned.map((ep) => {
 | 
				
			||||||
          return {
 | 
					        return {
 | 
				
			||||||
            ..._ep,
 | 
					          ...ep,
 | 
				
			||||||
            cleanUrl: this.getCleanEpisodeUrl(_ep.enclosure.url)
 | 
					          isDownloading: this.getIsEpisodeDownloadingOrQueued(ep),
 | 
				
			||||||
          }
 | 
					          isDownloaded: this.getIsEpisodeDownloaded(ep)
 | 
				
			||||||
        })
 | 
					        }
 | 
				
			||||||
      this.episodesCleaned.sort((a, b) => (a.publishedAt < b.publishedAt ? 1 : -1))
 | 
					      })
 | 
				
			||||||
      this.selectAll = false
 | 
					 | 
				
			||||||
      this.selectedEpisodes = {}
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  mounted() {}
 | 
					  mounted() {}
 | 
				
			||||||
 | 
				
			|||||||
@ -141,7 +141,7 @@
 | 
				
			|||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <modals-podcast-episode-feed v-model="showPodcastEpisodeFeed" :library-item="libraryItem" :episodes="podcastFeedEpisodes" />
 | 
					    <modals-podcast-episode-feed v-model="showPodcastEpisodeFeed" :library-item="libraryItem" :episodes="podcastFeedEpisodes" :download-queue="episodeDownloadsQueued" :episodes-downloading="episodesDownloading" />
 | 
				
			||||||
    <modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :playback-rate="1" :library-item-id="libraryItemId" hide-create @select="selectBookmark" />
 | 
					    <modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :playback-rate="1" :library-item-id="libraryItemId" hide-create @select="selectBookmark" />
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
@ -660,13 +660,11 @@ export default {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    rssFeedOpen(data) {
 | 
					    rssFeedOpen(data) {
 | 
				
			||||||
      if (data.entityId === this.libraryItemId) {
 | 
					      if (data.entityId === this.libraryItemId) {
 | 
				
			||||||
        console.log('RSS Feed Opened', data)
 | 
					 | 
				
			||||||
        this.rssFeed = data
 | 
					        this.rssFeed = data
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    rssFeedClosed(data) {
 | 
					    rssFeedClosed(data) {
 | 
				
			||||||
      if (data.entityId === this.libraryItemId) {
 | 
					      if (data.entityId === this.libraryItemId) {
 | 
				
			||||||
        console.log('RSS Feed Closed', data)
 | 
					 | 
				
			||||||
        this.rssFeed = null
 | 
					        this.rssFeed = null
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
				
			|||||||
@ -72,6 +72,15 @@ class PodcastManager {
 | 
				
			|||||||
   */
 | 
					   */
 | 
				
			||||||
  async startPodcastEpisodeDownload(podcastEpisodeDownload) {
 | 
					  async startPodcastEpisodeDownload(podcastEpisodeDownload) {
 | 
				
			||||||
    if (this.currentDownload) {
 | 
					    if (this.currentDownload) {
 | 
				
			||||||
 | 
					      // Prevent downloading episodes from the same URL for the same library item.
 | 
				
			||||||
 | 
					      // Allow downloading for different library items in case of the same podcast existing in multiple libraries (e.g. different folders)
 | 
				
			||||||
 | 
					      if (this.downloadQueue.some((d) => d.url === podcastEpisodeDownload.url && d.libraryItem.id === podcastEpisodeDownload.libraryItem.id)) {
 | 
				
			||||||
 | 
					        Logger.warn(`[PodcastManager] Episode already in queue: "${this.currentDownload.episodeTitle}"`)
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					      } else if (this.currentDownload.url === podcastEpisodeDownload.url && this.currentDownload.libraryItem.id === podcastEpisodeDownload.libraryItem.id) {
 | 
				
			||||||
 | 
					        Logger.warn(`[PodcastManager] Episode download already in progress for "${podcastEpisodeDownload.episodeTitle}"`)
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      this.downloadQueue.push(podcastEpisodeDownload)
 | 
					      this.downloadQueue.push(podcastEpisodeDownload)
 | 
				
			||||||
      SocketAuthority.emitter('episode_download_queued', podcastEpisodeDownload.toJSONForClient())
 | 
					      SocketAuthority.emitter('episode_download_queued', podcastEpisodeDownload.toJSONForClient())
 | 
				
			||||||
      return
 | 
					      return
 | 
				
			||||||
 | 
				
			|||||||
@ -43,7 +43,8 @@ class PodcastEpisodeDownload {
 | 
				
			|||||||
      season: this.rssPodcastEpisode?.season ?? null,
 | 
					      season: this.rssPodcastEpisode?.season ?? null,
 | 
				
			||||||
      episode: this.rssPodcastEpisode?.episode ?? null,
 | 
					      episode: this.rssPodcastEpisode?.episode ?? null,
 | 
				
			||||||
      episodeType: this.rssPodcastEpisode?.episodeType ?? 'full',
 | 
					      episodeType: this.rssPodcastEpisode?.episodeType ?? 'full',
 | 
				
			||||||
      publishedAt: this.rssPodcastEpisode?.publishedAt ?? null
 | 
					      publishedAt: this.rssPodcastEpisode?.publishedAt ?? null,
 | 
				
			||||||
 | 
					      guid: this.rssPodcastEpisode?.guid ?? null
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user