mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Show current episode download on init and download queue page updates
This commit is contained in:
		
							parent
							
								
									61c759e0c4
								
							
						
					
					
						commit
						022bf9d0ef
					
				| @ -10,7 +10,7 @@ | ||||
|       <div class="w-full"> | ||||
|         <table class="text-sm tracksTable"> | ||||
|           <tr> | ||||
|             <th class="text-left px-4">{{ $strings.LabelPodcast }}</th> | ||||
|             <th class="text-left px-4 min-w-48">{{ $strings.LabelPodcast }}</th> | ||||
|             <th class="text-left w-32 min-w-32">{{ $strings.LabelEpisode }}</th> | ||||
|             <th class="text-left px-4">{{ $strings.LabelEpisodeTitle }}</th> | ||||
|             <th class="text-left px-4 w-48">{{ $strings.LabelPubDate }}</th> | ||||
| @ -58,11 +58,8 @@ export default { | ||||
|   data() { | ||||
|     return {} | ||||
|   }, | ||||
|   computed: { | ||||
|   }, | ||||
|   methods: { | ||||
|   }, | ||||
|   mounted() { | ||||
|   } | ||||
|   computed: {}, | ||||
|   methods: {}, | ||||
|   mounted() {} | ||||
| } | ||||
| </script> | ||||
|  | ||||
| @ -14,8 +14,8 @@ | ||||
|             <template v-if="tasksRunningOrFailed.length"> | ||||
|               <p class="uppercase text-xs text-gray-400 my-1 px-1 font-semibold">{{ $strings.LabelTasks }}</p> | ||||
|               <template v-for="task in tasksRunningOrFailed"> | ||||
|                 <nuxt-link v-if="actionLink(task)" :to="actionLink(task)"> | ||||
|                   <li :key="task.id" class="text-gray-50 select-none relative hover:bg-black-400 py-1 cursor-pointer"> | ||||
|                 <nuxt-link :key="task.id" v-if="actionLink(task)" :to="actionLink(task)"> | ||||
|                   <li class="text-gray-50 select-none relative hover:bg-black-400 py-1 cursor-pointer"> | ||||
|                     <cards-item-task-running-card :task="task" /> | ||||
|                   </li> | ||||
|                 </nuxt-link> | ||||
|  | ||||
| @ -319,7 +319,7 @@ export default { | ||||
|       return this.libraryItem.isInvalid | ||||
|     }, | ||||
|     isExplicit() { | ||||
|       return this.mediaMetadata.explicit || false; | ||||
|       return this.mediaMetadata.explicit || false | ||||
|     }, | ||||
|     invalidAudioFiles() { | ||||
|       if (!this.isBook) return [] | ||||
| @ -759,9 +759,8 @@ export default { | ||||
|     } | ||||
|   }, | ||||
|   mounted() { | ||||
|     if (this.libraryItem.episodesDownloading) { | ||||
|       this.episodeDownloadsQueued = this.libraryItem.episodesDownloading || [] | ||||
|     } | ||||
|     this.episodeDownloadsQueued = this.libraryItem.episodeDownloadsQueued || [] | ||||
|     this.episodesDownloading = this.libraryItem.episodesDownloading || [] | ||||
| 
 | ||||
|     // use this items library id as the current | ||||
|     if (this.libraryId) { | ||||
|  | ||||
| @ -3,10 +3,9 @@ | ||||
|     <app-book-shelf-toolbar page="podcast-search" /> | ||||
| 
 | ||||
|     <div id="bookshelf" class="w-full overflow-y-auto px-2 py-6 sm:px-4 md:p-12 relative"> | ||||
| 
 | ||||
|       <div class="w-full max-w-3xl mx-auto py-4"> | ||||
|       <div class="w-full max-w-5xl mx-auto py-4"> | ||||
|         <p class="text-xl mb-2 font-semibold px-4 md:px-0">{{ $strings.HeaderCurrentDownloads }}</p> | ||||
|         <p v-if="!episodesDownloading.length" class="text-center text-xl">{{ $strings.MessageNoDownloadsInProgress }}</p> | ||||
|         <p v-if="!episodesDownloading.length" class="text-lg py-4">{{ $strings.MessageNoDownloadsInProgress }}</p> | ||||
|         <template v-for="episode in episodesDownloading"> | ||||
|           <div :key="episode.id" class="flex py-5 relative"> | ||||
|             <covers-preview-cover :src="$store.getters['globals/getLibraryItemCoverSrcById'](episode.libraryItemId)" :width="96" :book-cover-aspect-ratio="bookCoverAspectRatio" :show-resolution="false" class="hidden md:block" /> | ||||
| @ -46,17 +45,15 @@ | ||||
|             </div> | ||||
|           </div> | ||||
|         </template> | ||||
|       </div> | ||||
| 
 | ||||
|       <tables-podcast-download-queue-table :queue="episodeDownloadsQueued"></tables-podcast-download-queue-table> | ||||
|         <tables-podcast-download-queue-table v-if="episodeDownloadsQueued.length" :queue="episodeDownloadsQueued"></tables-podcast-download-queue-table> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| <script> | ||||
| import DownloadQueueTable from "~/components/tables/podcast/DownloadQueueTable.vue"; | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|   components: {DownloadQueueTable}, | ||||
|   async asyncData({ params, redirect }) { | ||||
|     if (!params.library) { | ||||
|       console.error('No library...', params.library) | ||||
| @ -70,7 +67,7 @@ export default { | ||||
|     return { | ||||
|       episodesDownloading: [], | ||||
|       episodeDownloadsQueued: [], | ||||
|       processing: false, | ||||
|       processing: false | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
| @ -99,35 +96,45 @@ export default { | ||||
|         this.episodesDownloading = this.episodesDownloading.filter((d) => d.id !== episodeDownload.id) | ||||
|       } | ||||
|     }, | ||||
|     downloadQueueUpdated(downloadQueue) { | ||||
|       this.episodeDownloadsQueued = downloadQueue.filter((q) => q.libraryId == this.libraryId) | ||||
|     episodeDownloadQueueUpdated(downloadQueueDetails) { | ||||
|       this.episodeDownloadsQueued = downloadQueueDetails.queue.filter((q) => q.libraryId == this.libraryId) | ||||
|     }, | ||||
|     async loadInitialDownloadQueue() { | ||||
|       this.processing = true | ||||
|       const queuePayload = await this.$axios.$get(`/api/libraries/${this.libraryId}/downloads`).catch((error) => { | ||||
|       const queuePayload = await this.$axios.$get(`/api/libraries/${this.libraryId}/episode-downloads`).catch((error) => { | ||||
|         console.error('Failed to get download queue', error) | ||||
|         this.$toast.error('Failed to get download queue') | ||||
|         return null | ||||
|       }) | ||||
|       this.processing = false | ||||
|       this.episodeDownloadsQueued = queuePayload || [] | ||||
|       this.episodeDownloadsQueued = queuePayload?.queue || [] | ||||
| 
 | ||||
|       if (queuePayload?.currentDownload) { | ||||
|         this.episodesDownloading.push(queuePayload.currentDownload) | ||||
|       } | ||||
| 
 | ||||
|       // Initialize listeners after load to prevent event race conditions | ||||
|       this.initListeners() | ||||
|     }, | ||||
|     initListeners() { | ||||
|       this.$root.socket.on('episode_download_queued', this.episodeDownloadQueued) | ||||
|       this.$root.socket.on('episode_download_started', this.episodeDownloadStarted) | ||||
|       this.$root.socket.on('episode_download_finished', this.episodeDownloadFinished) | ||||
|       this.$root.socket.on('episode_download_queue_updated', this.episodeDownloadQueueUpdated) | ||||
|     } | ||||
|   }, | ||||
|   mounted() { | ||||
|     if (this.libraryId) { | ||||
|       this.$store.commit('libraries/setCurrentLibrary', this.libraryId) | ||||
|     } | ||||
| 
 | ||||
|     this.loadInitialDownloadQueue() | ||||
|     this.$root.socket.on('episode_download_queued', this.episodeDownloadQueued) | ||||
|     this.$root.socket.on('episode_download_started', this.episodeDownloadStarted) | ||||
|     this.$root.socket.on('episode_download_finished', this.episodeDownloadFinished) | ||||
|     this.$root.socket.on('download_queue_updated', this.downloadQueueUpdated) | ||||
|   }, | ||||
|   beforeDestroy() { | ||||
|     this.$root.socket.off('episode_download_queued', this.episodeDownloadQueued) | ||||
|     this.$root.socket.off('episode_download_started', this.episodeDownloadStarted) | ||||
|     this.$root.socket.off('episode_download_finished', this.episodeDownloadFinished) | ||||
|     this.$root.socket.off('download_queue_updated', this.downloadQueueUpdated) | ||||
|     this.$root.socket.off('episode_download_queue_updated', this.episodeDownloadQueueUpdated) | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| @ -15,7 +15,9 @@ | ||||
|                 <covers-preview-cover :src="$store.getters['globals/getLibraryItemCoverSrcById'](episode.libraryItemId)" :width="48" :book-cover-aspect-ratio="bookCoverAspectRatio" :show-resolution="false" class="md:hidden" /> | ||||
|                 <div class="flex-grow px-2"> | ||||
|                   <div class="flex items-center"> | ||||
|                     <div class="flex" @click.stop> | ||||
|                       <nuxt-link :to="`/item/${episode.libraryItemId}`" class="text-sm text-gray-200 hover:underline">{{ episode.podcast.metadata.title }}</nuxt-link> | ||||
|                     </div> | ||||
|                     <widgets-explicit-indicator :explicit="episode.podcast.metadata.explicit" /> | ||||
|                   </div> | ||||
|                   <p class="text-xs text-gray-300 mb-1">{{ $dateDistanceFromNow(episode.publishedAt) }}</p> | ||||
| @ -24,7 +26,9 @@ | ||||
|               <!-- desktop --> | ||||
|               <div class="hidden md:block"> | ||||
|                 <div class="flex items-center"> | ||||
|                   <div class="flex" @click.stop> | ||||
|                     <nuxt-link :to="`/item/${episode.libraryItemId}`" class="text-sm text-gray-200 hover:underline">{{ episode.podcast.metadata.title }}</nuxt-link> | ||||
|                   </div> | ||||
|                   <widgets-explicit-indicator :explicit="episode.podcast.metadata.explicit" /> | ||||
|                 </div> | ||||
|                 <p class="text-xs text-gray-300 mb-1">{{ $dateDistanceFromNow(episode.publishedAt) }}</p> | ||||
|  | ||||
| @ -82,11 +82,9 @@ class LibraryController { | ||||
|     return res.json(req.library) | ||||
|   } | ||||
| 
 | ||||
|   async getDownloadQueue(req, res) { | ||||
|     const library = req.library | ||||
| 
 | ||||
|     let queue = this.podcastManager.getDownloadQueueDetails().filter(q => q.libraryId === library.id) | ||||
|     return res.json(queue) | ||||
|   async getEpisodeDownloadQueue(req, res) { | ||||
|     const libraryDownloadQueueDetails = this.podcastManager.getDownloadQueueDetails(req.library.id) | ||||
|     return res.json(libraryDownloadQueueDetails) | ||||
|   } | ||||
| 
 | ||||
|   async update(req, res) { | ||||
|  | ||||
| @ -36,8 +36,11 @@ class LibraryItemController { | ||||
|           }).filter(au => au) | ||||
|         } | ||||
|       } else if (includeEntities.includes('downloads')) { | ||||
|         var downloadsInQueue = this.podcastManager.getEpisodeDownloadsInQueue(req.libraryItem.id) | ||||
|         item.episodesDownloading = downloadsInQueue.map(d => d.toJSONForClient()) | ||||
|         const downloadsInQueue = this.podcastManager.getEpisodeDownloadsInQueue(req.libraryItem.id) | ||||
|         item.episodeDownloadsQueued = downloadsInQueue.map(d => d.toJSONForClient()) | ||||
|         if (this.podcastManager.currentDownload?.libraryItemId === req.libraryItem.id) { | ||||
|           item.episodesDownloading = [this.podcastManager.currentDownload.toJSONForClient()] | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       return res.json(item) | ||||
|  | ||||
| @ -14,8 +14,7 @@ const LibraryFile = require('../objects/files/LibraryFile') | ||||
| const PodcastEpisodeDownload = require('../objects/PodcastEpisodeDownload') | ||||
| const PodcastEpisode = require('../objects/entities/PodcastEpisode') | ||||
| const AudioFile = require('../objects/files/AudioFile') | ||||
| const Task = require("../objects/Task"); | ||||
| const Path = require("path"); | ||||
| const Task = require("../objects/Task") | ||||
| 
 | ||||
| class PodcastManager { | ||||
|   constructor(db, watcher, notificationManager, taskManager) { | ||||
| @ -60,12 +59,12 @@ class PodcastManager { | ||||
|       newPe.libraryItemId = libraryItem.id | ||||
|       var newPeDl = new PodcastEpisodeDownload() | ||||
|       newPeDl.setData(newPe, libraryItem, isAutoDownload, libraryItem.libraryId) | ||||
|       this.startPodcastEpisodeDownload(newPeDl, libraryItem) | ||||
|       this.startPodcastEpisodeDownload(newPeDl) | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   async startPodcastEpisodeDownload(podcastEpisodeDownload, libraryItem) { | ||||
|     SocketAuthority.emitter('download_queue_updated', this.getDownloadQueueDetails()) | ||||
|   async startPodcastEpisodeDownload(podcastEpisodeDownload) { | ||||
|     SocketAuthority.emitter('episode_download_queue_updated', this.getDownloadQueueDetails()) | ||||
|     if (this.currentDownload) { | ||||
|       this.downloadQueue.push(podcastEpisodeDownload) | ||||
|       SocketAuthority.emitter('episode_download_queued', podcastEpisodeDownload.toJSONForClient()) | ||||
| @ -75,8 +74,8 @@ class PodcastManager { | ||||
|     const task = new Task() | ||||
|     const taskDescription = `Downloading episode "${podcastEpisodeDownload.podcastEpisode.title}".` | ||||
|     const taskData = { | ||||
|       libraryId: libraryItem.libraryId, | ||||
|       libraryItemId: libraryItem.id, | ||||
|       libraryId: podcastEpisodeDownload.libraryId, | ||||
|       libraryItemId: podcastEpisodeDownload.libraryItemId, | ||||
|     } | ||||
|     task.setData('download-podcast-episode', 'Downloading Episode', taskDescription, taskData) | ||||
|     this.taskManager.addTask(task) | ||||
| @ -94,7 +93,7 @@ class PodcastManager { | ||||
|       await filePerms.setDefault(this.currentDownload.libraryItem.path) | ||||
|     } | ||||
| 
 | ||||
|     var success = await downloadFile(this.currentDownload.url, this.currentDownload.targetPath).then(() => true).catch((error) => { | ||||
|     let success = await downloadFile(this.currentDownload.url, this.currentDownload.targetPath).then(() => true).catch((error) => { | ||||
|       Logger.error(`[PodcastManager] Podcast Episode download failed`, error) | ||||
|       return false | ||||
|     }) | ||||
| @ -117,12 +116,12 @@ class PodcastManager { | ||||
|     this.taskManager.taskFinished(task) | ||||
| 
 | ||||
|     SocketAuthority.emitter('episode_download_finished', this.currentDownload.toJSONForClient()) | ||||
|     SocketAuthority.emitter('download_queue_updated', this.getDownloadQueueDetails()) | ||||
|     SocketAuthority.emitter('episode_download_queue_updated', this.getDownloadQueueDetails()) | ||||
| 
 | ||||
|     this.watcher.removeIgnoreDir(this.currentDownload.libraryItem.path) | ||||
|     this.currentDownload = null | ||||
|     if (this.downloadQueue.length) { | ||||
|       this.startPodcastEpisodeDownload(this.downloadQueue.shift(), libraryItem) | ||||
|       this.startPodcastEpisodeDownload(this.downloadQueue.shift()) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| @ -349,21 +348,14 @@ class PodcastManager { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   getDownloadQueueDetails() { | ||||
|     return this.downloadQueue.map(item => { | ||||
|   getDownloadQueueDetails(libraryId = null) { | ||||
|     let _currentDownload = this.currentDownload | ||||
|     if (libraryId && _currentDownload?.libraryId !== libraryId) _currentDownload = null | ||||
| 
 | ||||
|     return { | ||||
|         id: item.id, | ||||
|         libraryId: item.libraryId || null, | ||||
|         libraryItemId: item.libraryItemId || null, | ||||
|         podcastTitle: item.libraryItem.media.metadata.title || null, | ||||
|         podcastExplicit: item.libraryItem.media.metadata.explicit || false, | ||||
|         episodeDisplayTitle: item.podcastEpisode.title || null, | ||||
|         season: item.podcastEpisode.season || null, | ||||
|         episode: item.podcastEpisode.episode || null, | ||||
|         episodeType: item.podcastEpisode.episodeType || 'full', | ||||
|         publishedAt: item.podcastEpisode.publishedAt || null | ||||
|       currentDownload: _currentDownload?.toJSONForClient(), | ||||
|       queue: this.downloadQueue.filter(item => !libraryId || item.libraryId === libraryId).map(item => item.toJSONForClient()) | ||||
|     } | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| module.exports = PodcastManager | ||||
|  | ||||
| @ -11,7 +11,6 @@ class PodcastEpisodeDownload { | ||||
|     this.libraryId = null | ||||
| 
 | ||||
|     this.isAutoDownload = false | ||||
|     this.isDownloading = false | ||||
|     this.isFinished = false | ||||
|     this.failed = false | ||||
| 
 | ||||
| @ -23,20 +22,21 @@ class PodcastEpisodeDownload { | ||||
|   toJSONForClient() { | ||||
|     return { | ||||
|       id: this.id, | ||||
|       episodeDisplayTitle: this.podcastEpisode ? this.podcastEpisode.title : null, | ||||
|       episodeDisplayTitle: this.podcastEpisode?.title ?? null, | ||||
|       url: this.url, | ||||
|       libraryItemId: this.libraryItem ? this.libraryItem.id : null, | ||||
|       libraryItemId: this.libraryItem?.id || null, | ||||
|       libraryId: this.libraryId || null, | ||||
|       isDownloading: this.isDownloading, | ||||
|       isFinished: this.isFinished, | ||||
|       failed: this.failed, | ||||
|       startedAt: this.startedAt, | ||||
|       createdAt: this.createdAt, | ||||
|       finishedAt: this.finishedAt, | ||||
|       season: this.podcastEpisode ? this.podcastEpisode.season : null, | ||||
|       episode: this.podcastEpisode ? this.podcastEpisode.episode : null, | ||||
|       episodeType: this.podcastEpisode ? this.podcastEpisode.episodeType : 'full', | ||||
|       publishedAt: this.podcastEpisode ? this.podcastEpisode.publishedAt : null | ||||
|       podcastTitle: this.libraryItem?.media.metadata.title ?? null, | ||||
|       podcastExplicit: !!this.libraryItem?.media.metadata.explicit, | ||||
|       season: this.podcastEpisode?.season ?? null, | ||||
|       episode: this.podcastEpisode?.episode ?? null, | ||||
|       episodeType: this.podcastEpisode?.episodeType ?? 'full', | ||||
|       publishedAt: this.podcastEpisode?.publishedAt ?? null | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -76,7 +76,7 @@ class ApiRouter { | ||||
| 
 | ||||
|     this.router.get('/libraries/:id/items', LibraryController.middleware.bind(this), LibraryController.getLibraryItems.bind(this)) | ||||
|     this.router.delete('/libraries/:id/issues', LibraryController.middleware.bind(this), LibraryController.removeLibraryItemsWithIssues.bind(this)) | ||||
|     this.router.get('/libraries/:id/downloads', LibraryController.middleware.bind(this), LibraryController.getDownloadQueue.bind(this)) | ||||
|     this.router.get('/libraries/:id/episode-downloads', LibraryController.middleware.bind(this), LibraryController.getEpisodeDownloadQueue.bind(this)) | ||||
|     this.router.get('/libraries/:id/series', LibraryController.middleware.bind(this), LibraryController.getAllSeriesForLibrary.bind(this)) | ||||
|     this.router.get('/libraries/:id/collections', LibraryController.middleware.bind(this), LibraryController.getCollectionsForLibrary.bind(this)) | ||||
|     this.router.get('/libraries/:id/playlists', LibraryController.middleware.bind(this), LibraryController.getUserPlaylistsForLibrary.bind(this)) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user