mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Add:Batch select audiobook play button, item page mobile screen size cleanup
This commit is contained in:
		
							parent
							
								
									8931702f1b
								
							
						
					
					
						commit
						7485cf1a26
					
				| @ -26,7 +26,7 @@ | |||||||
|   -webkit-font-smoothing: antialiased; |   -webkit-font-smoothing: antialiased; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .material-icons:not(.text-xs):not(.text-sm):not(.text-md):not(.text-base):not(.text-lg):not(.text-xl):not(.text-2xl):not(.text-3xl):not(.text-4xl):not(.text-5xl):not(.text-6xl):not(.text-7xl):not(.text-8xl) { | .material-icons:not([class*="text-"]) { | ||||||
|   font-size: 1.5rem; |   font-size: 1.5rem; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -44,11 +44,10 @@ | |||||||
|   -webkit-font-smoothing: antialiased; |   -webkit-font-smoothing: antialiased; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .material-icons-outlined:not(.text-xs):not(.text-sm):not(.text-md):not(.text-base):not(.text-lg):not(.text-xl):not(.text-2xl):not(.text-3xl):not(.text-4xl):not(.text-5xl):not(.text-6xl):not(.text-7xl):not(.text-8xl) { | .material-icons-outlined:not([class*="text-"]) { | ||||||
|   font-size: 1.5rem; |   font-size: 1.5rem; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| @font-face { | @font-face { | ||||||
|   font-family: 'Gentium Book Basic'; |   font-family: 'Gentium Book Basic'; | ||||||
|   font-style: normal; |   font-style: normal; | ||||||
|  | |||||||
| @ -46,8 +46,12 @@ | |||||||
|         </nuxt-link> |         </nuxt-link> | ||||||
|       </div> |       </div> | ||||||
|       <div v-show="numLibraryItemsSelected" class="absolute top-0 left-0 w-full h-full px-4 bg-primary flex items-center"> |       <div v-show="numLibraryItemsSelected" class="absolute top-0 left-0 w-full h-full px-4 bg-primary flex items-center"> | ||||||
|         <h1 class="text-2xl px-4">{{ $getString('MessageItemsSelected', [numLibraryItemsSelected]) }}</h1> |         <h1 class="text-lg md:text-2xl px-4">{{ $getString('MessageItemsSelected', [numLibraryItemsSelected]) }}</h1> | ||||||
|         <div class="flex-grow" /> |         <div class="flex-grow" /> | ||||||
|  |         <ui-btn v-if="!isPodcastLibrary" color="success" :padding-x="4" small class="flex items-center h-9 mr-2" @click="playSelectedItems"> | ||||||
|  |           <span class="material-icons -ml-2 pr-1 text-white">play_arrow</span> | ||||||
|  |           {{ $strings.ButtonPlay }} | ||||||
|  |         </ui-btn> | ||||||
|         <ui-tooltip v-if="userIsAdminOrUp && !isPodcastLibrary" :text="$strings.ButtonQuickMatch" direction="bottom"> |         <ui-tooltip v-if="userIsAdminOrUp && !isPodcastLibrary" :text="$strings.ButtonQuickMatch" direction="bottom"> | ||||||
|           <ui-icon-btn :disabled="processingBatch" icon="auto_awesome" @click="batchAutoMatchClick" class="mx-1.5" /> |           <ui-icon-btn :disabled="processingBatch" icon="auto_awesome" @click="batchAutoMatchClick" class="mx-1.5" /> | ||||||
|         </ui-tooltip> |         </ui-tooltip> | ||||||
| @ -59,14 +63,14 @@ | |||||||
|         </ui-tooltip> |         </ui-tooltip> | ||||||
|         <template v-if="userCanUpdate && numLibraryItemsSelected < 50"> |         <template v-if="userCanUpdate && numLibraryItemsSelected < 50"> | ||||||
|           <ui-tooltip text="Edit" direction="bottom"> |           <ui-tooltip text="Edit" direction="bottom"> | ||||||
|             <ui-icon-btn v-show="!processingBatchDelete" icon="edit" bg-color="warning" class="mx-1.5" @click="batchEditClick" /> |             <ui-icon-btn :disabled="processingBatch" icon="edit" bg-color="warning" class="mx-1.5" @click="batchEditClick" /> | ||||||
|           </ui-tooltip> |           </ui-tooltip> | ||||||
|         </template> |         </template> | ||||||
|         <ui-tooltip v-if="userCanDelete" :text="$strings.ButtonRemove" direction="bottom"> |         <ui-tooltip v-if="userCanDelete" :text="$strings.ButtonRemove" direction="bottom"> | ||||||
|           <ui-icon-btn :disabled="processingBatchDelete" icon="delete" bg-color="error" class="mx-1.5" @click="batchDeleteClick" /> |           <ui-icon-btn :disabled="processingBatch" icon="delete" bg-color="error" class="mx-1.5" @click="batchDeleteClick" /> | ||||||
|         </ui-tooltip> |         </ui-tooltip> | ||||||
|         <ui-tooltip :text="$strings.LabelDeselectAll" direction="bottom"> |         <ui-tooltip :text="$strings.LabelDeselectAll" direction="bottom"> | ||||||
|           <span class="material-icons text-4xl px-4 hover:text-gray-100 cursor-pointer" :class="processingBatchDelete ? 'text-gray-400' : ''" @click="cancelSelectionMode">close</span> |           <span class="material-icons text-4xl px-4 hover:text-gray-100 cursor-pointer" :class="processingBatch ? 'text-gray-400' : ''" @click="cancelSelectionMode">close</span> | ||||||
|         </ui-tooltip> |         </ui-tooltip> | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
| @ -77,9 +81,7 @@ | |||||||
| export default { | export default { | ||||||
|   data() { |   data() { | ||||||
|     return { |     return { | ||||||
|       processingBatchDelete: false, |       totalEntities: 0 | ||||||
|       totalEntities: 0, |  | ||||||
|       isAllSelected: false |  | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   computed: { |   computed: { | ||||||
| @ -149,11 +151,47 @@ export default { | |||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   methods: { |   methods: { | ||||||
|     cancelSelectionMode() { |     async playSelectedItems() { | ||||||
|       if (this.processingBatchDelete) return |       this.$store.commit('setProcessingBatch', true) | ||||||
|  | 
 | ||||||
|  |       var libraryItems = await this.$axios.$post(`/api/items/batch/get`, { libraryItemIds: this.selectedLibraryItems }).catch((error) => { | ||||||
|  |         var errorMsg = error.response.data || 'Failed to get items' | ||||||
|  |         console.error(errorMsg, error) | ||||||
|  |         this.$toast.error(errorMsg) | ||||||
|  |         return [] | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       if (!libraryItems.length) { | ||||||
|  |         this.$store.commit('setProcessingBatch', false) | ||||||
|  |         return | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       const queueItems = [] | ||||||
|  |       libraryItems.forEach((item) => { | ||||||
|  |         queueItems.push({ | ||||||
|  |           libraryItemId: item.id, | ||||||
|  |           libraryId: item.libraryId, | ||||||
|  |           episodeId: null, | ||||||
|  |           title: item.media.metadata.title, | ||||||
|  |           subtitle: item.media.metadata.authors.map((au) => au.name).join(', '), | ||||||
|  |           caption: '', | ||||||
|  |           duration: item.media.duration || null, | ||||||
|  |           coverPath: item.media.coverPath || null | ||||||
|  |         }) | ||||||
|  |       }) | ||||||
|  | 
 | ||||||
|  |       this.$eventBus.$emit('play-item', { | ||||||
|  |         libraryItemId: queueItems[0].libraryItemId, | ||||||
|  |         queueItems | ||||||
|  |       }) | ||||||
|  |       this.$store.commit('setProcessingBatch', false) | ||||||
|  |       this.$store.commit('setSelectedLibraryItems', []) | ||||||
|  |       this.$eventBus.$emit('bookshelf_clear_selection') | ||||||
|  |     }, | ||||||
|  |     cancelSelectionMode() { | ||||||
|  |       if (this.processingBatch) return | ||||||
|       this.$store.commit('setSelectedLibraryItems', []) |       this.$store.commit('setSelectedLibraryItems', []) | ||||||
|       this.$eventBus.$emit('bookshelf_clear_selection') |       this.$eventBus.$emit('bookshelf_clear_selection') | ||||||
|       this.isAllSelected = false |  | ||||||
|     }, |     }, | ||||||
|     toggleBatchRead() { |     toggleBatchRead() { | ||||||
|       this.$store.commit('setProcessingBatch', true) |       this.$store.commit('setProcessingBatch', true) | ||||||
| @ -183,7 +221,6 @@ export default { | |||||||
|       var audiobookText = this.numLibraryItemsSelected > 1 ? `these ${this.numLibraryItemsSelected} items` : 'this item' |       var audiobookText = this.numLibraryItemsSelected > 1 ? `these ${this.numLibraryItemsSelected} items` : 'this item' | ||||||
|       var confirmMsg = `Are you sure you want to remove ${audiobookText}?\n\n*Does not delete your files, only removes the items from Audiobookshelf` |       var confirmMsg = `Are you sure you want to remove ${audiobookText}?\n\n*Does not delete your files, only removes the items from Audiobookshelf` | ||||||
|       if (confirm(confirmMsg)) { |       if (confirm(confirmMsg)) { | ||||||
|         this.processingBatchDelete = true |  | ||||||
|         this.$store.commit('setProcessingBatch', true) |         this.$store.commit('setProcessingBatch', true) | ||||||
|         this.$axios |         this.$axios | ||||||
|           .$post(`/api/items/batch/delete`, { |           .$post(`/api/items/batch/delete`, { | ||||||
| @ -191,7 +228,6 @@ export default { | |||||||
|           }) |           }) | ||||||
|           .then(() => { |           .then(() => { | ||||||
|             this.$toast.success('Batch delete success!') |             this.$toast.success('Batch delete success!') | ||||||
|             this.processingBatchDelete = false |  | ||||||
|             this.$store.commit('setProcessingBatch', false) |             this.$store.commit('setProcessingBatch', false) | ||||||
|             this.$store.commit('setSelectedLibraryItems', []) |             this.$store.commit('setSelectedLibraryItems', []) | ||||||
|             this.$eventBus.$emit('bookshelf_clear_selection') |             this.$eventBus.$emit('bookshelf_clear_selection') | ||||||
| @ -199,7 +235,6 @@ export default { | |||||||
|           .catch((error) => { |           .catch((error) => { | ||||||
|             this.$toast.error('Batch delete failed') |             this.$toast.error('Batch delete failed') | ||||||
|             console.error('Failed to batch delete', error) |             console.error('Failed to batch delete', error) | ||||||
|             this.processingBatchDelete = false |  | ||||||
|             this.$store.commit('setProcessingBatch', false) |             this.$store.commit('setProcessingBatch', false) | ||||||
|           }) |           }) | ||||||
|       } |       } | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ | |||||||
|         <controls-volume-control ref="volumeControl" v-model="volume" @input="setVolume" class="mx-2 hidden md:block" /> |         <controls-volume-control ref="volumeControl" v-model="volume" @input="setVolume" class="mx-2 hidden md:block" /> | ||||||
| 
 | 
 | ||||||
|         <div class="cursor-pointer text-gray-300 hover:text-white mx-1 lg:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showSleepTimer')"> |         <div class="cursor-pointer text-gray-300 hover:text-white mx-1 lg:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showSleepTimer')"> | ||||||
|           <span v-if="!sleepTimerSet" class="material-icons text-2xl sm:text-2.5xl">snooze</span> |           <span v-if="!sleepTimerSet" class="material-icons text-2xl">snooze</span> | ||||||
|           <div v-else class="flex items-center"> |           <div v-else class="flex items-center"> | ||||||
|             <span class="material-icons text-lg text-warning">snooze</span> |             <span class="material-icons text-lg text-warning">snooze</span> | ||||||
|             <p class="text-xl text-warning font-mono font-semibold text-center px-0.5 pb-0.5" style="min-width: 30px">{{ sleepTimerRemainingString }}</p> |             <p class="text-xl text-warning font-mono font-semibold text-center px-0.5 pb-0.5" style="min-width: 30px">{{ sleepTimerRemainingString }}</p> | ||||||
| @ -15,15 +15,15 @@ | |||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <div v-if="!isPodcast" class="cursor-pointer text-gray-300 hover:text-white mx-1 lg:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showBookmarks')"> |         <div v-if="!isPodcast" class="cursor-pointer text-gray-300 hover:text-white mx-1 lg:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showBookmarks')"> | ||||||
|           <span class="material-icons text-2xl sm:text-2.5xl">{{ bookmarks.length ? 'bookmarks' : 'bookmark_border' }}</span> |           <span class="material-icons text-2xl">{{ bookmarks.length ? 'bookmarks' : 'bookmark_border' }}</span> | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <div v-if="chapters.length" class="cursor-pointer text-gray-300 hover:text-white mx-1 lg:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="showChapters"> |         <div v-if="chapters.length" class="cursor-pointer text-gray-300 hover:text-white mx-1 lg:mx-2" @mousedown.prevent @mouseup.prevent @click.stop="showChapters"> | ||||||
|           <span class="material-icons text-2xl sm:text-3xl">format_list_bulleted</span> |           <span class="material-icons text-2xl">format_list_bulleted</span> | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <button v-if="playerQueueItems.length" class="outline-none text-gray-300 mx-1 lg:mx-2 hover:text-white" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showPlayerQueueItems')"> |         <button v-if="playerQueueItems.length" class="outline-none text-gray-300 mx-1 lg:mx-2 hover:text-white" @mousedown.prevent @mouseup.prevent @click.stop="$emit('showPlayerQueueItems')"> | ||||||
|           <span class="material-icons text-2xl sm:text-3xl">queue_music</span> |           <span class="material-icons text-2.5xl sm:text-3xl">queue_music</span> | ||||||
|         </button> |         </button> | ||||||
| 
 | 
 | ||||||
|         <ui-tooltip v-if="chapters.length" direction="top" :text="useChapterTrack ? $strings.LabelUseFullTrack : $strings.LabelUseChapterTrack"> |         <ui-tooltip v-if="chapters.length" direction="top" :text="useChapterTrack ? $strings.LabelUseFullTrack : $strings.LabelUseChapterTrack"> | ||||||
|  | |||||||
| @ -34,7 +34,7 @@ | |||||||
| 
 | 
 | ||||||
|               <template v-if="!isVideo"> |               <template v-if="!isVideo"> | ||||||
|                 <p v-if="isPodcast" class="mb-2 mt-0.5 text-gray-200 text-lg md:text-xl">by {{ podcastAuthor || 'Unknown' }}</p> |                 <p v-if="isPodcast" class="mb-2 mt-0.5 text-gray-200 text-lg md:text-xl">by {{ podcastAuthor || 'Unknown' }}</p> | ||||||
|                 <p v-else-if="authors.length" class="mb-2 mt-0.5 text-gray-200 text-lg md:text-xl"> |                 <p v-else-if="authors.length" class="mb-2 mt-0.5 text-gray-200 text-lg md:text-xl max-w-[calc(100vw-2rem)] overflow-hidden overflow-ellipsis"> | ||||||
|                   by <nuxt-link v-for="(author, index) in authors" :key="index" :to="`/author/${author.id}`" class="hover:underline">{{ author.name }}<span v-if="index < authors.length - 1">, </span></nuxt-link> |                   by <nuxt-link v-for="(author, index) in authors" :key="index" :to="`/author/${author.id}`" class="hover:underline">{{ author.name }}<span v-if="index < authors.length - 1">, </span></nuxt-link> | ||||||
|                 </p> |                 </p> | ||||||
|                 <p v-else class="mb-2 mt-0.5 text-gray-200 text-xl">by Unknown</p> |                 <p v-else class="mb-2 mt-0.5 text-gray-200 text-xl">by Unknown</p> | ||||||
| @ -44,7 +44,7 @@ | |||||||
|                 <div class="w-32"> |                 <div class="w-32"> | ||||||
|                   <span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelNarrators }}</span> |                   <span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelNarrators }}</span> | ||||||
|                 </div> |                 </div> | ||||||
|                 <div> |                 <div class="max-w-[calc(100vw-10rem)] overflow-hidden overflow-ellipsis"> | ||||||
|                   <template v-for="(narrator, index) in narrators"> |                   <template v-for="(narrator, index) in narrators"> | ||||||
|                     <nuxt-link :key="narrator" :to="`/library/${libraryId}/bookshelf?filter=narrators.${$encode(narrator)}`" class="hover:underline">{{ narrator }}</nuxt-link |                     <nuxt-link :key="narrator" :to="`/library/${libraryId}/bookshelf?filter=narrators.${$encode(narrator)}`" class="hover:underline">{{ narrator }}</nuxt-link | ||||||
|                     ><span :key="index" v-if="index < narrators.length - 1">, </span> |                     ><span :key="index" v-if="index < narrators.length - 1">, </span> | ||||||
| @ -63,7 +63,7 @@ | |||||||
|                 <div class="w-32"> |                 <div class="w-32"> | ||||||
|                   <span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelGenres }}</span> |                   <span class="text-white text-opacity-60 uppercase text-sm">{{ $strings.LabelGenres }}</span> | ||||||
|                 </div> |                 </div> | ||||||
|                 <div> |                 <div class="max-w-[calc(100vw-10rem)] overflow-hidden overflow-ellipsis"> | ||||||
|                   <template v-for="(genre, index) in genres"> |                   <template v-for="(genre, index) in genres"> | ||||||
|                     <nuxt-link :key="genre" :to="`/library/${libraryId}/bookshelf?filter=genres.${$encode(genre)}`" class="hover:underline">{{ genre }}</nuxt-link |                     <nuxt-link :key="genre" :to="`/library/${libraryId}/bookshelf?filter=genres.${$encode(genre)}`" class="hover:underline">{{ genre }}</nuxt-link | ||||||
|                     ><span :key="index" v-if="index < genres.length - 1">, </span> |                     ><span :key="index" v-if="index < genres.length - 1">, </span> | ||||||
|  | |||||||
| @ -301,7 +301,11 @@ class LibraryItemController { | |||||||
|     if (!libraryItemIds.length) { |     if (!libraryItemIds.length) { | ||||||
|       return res.status(403).send('Invalid payload') |       return res.status(403).send('Invalid payload') | ||||||
|     } |     } | ||||||
|     var libraryItems = this.db.libraryItems.filter(li => libraryItemIds.includes(li.id)).map((li) => li.toJSONExpanded()) |     var libraryItems = [] | ||||||
|  |     libraryItemIds.forEach((lid) => { | ||||||
|  |       const li = this.db.libraryItems.find(_li => _li.id === lid) | ||||||
|  |       if (li) libraryItems.push(li.toJSONExpanded()) | ||||||
|  |     }) | ||||||
|     res.json(libraryItems) |     res.json(libraryItems) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user