mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Add book item more menu item for Share, restrict share to admin or up, add admin socket events for open/close shares
This commit is contained in:
		
							parent
							
								
									e05ab14ad2
								
							
						
					
					
						commit
						2f2ec2ec1f
					
				| @ -601,6 +601,30 @@ export default { | ||||
|         this.executeRebuild() | ||||
|       } | ||||
|     }, | ||||
|     shareOpen(mediaItemShare) { | ||||
|       if (this.entityName === 'items' || this.entityName === 'series-books') { | ||||
|         var indexOf = this.entities.findIndex((ent) => ent?.media?.id === mediaItemShare.mediaItemId) | ||||
|         if (indexOf >= 0) { | ||||
|           if (this.entityComponentRefs[indexOf]) { | ||||
|             const libraryItem = { ...this.entityComponentRefs[indexOf].libraryItem } | ||||
|             libraryItem.mediaItemShare = mediaItemShare | ||||
|             this.entityComponentRefs[indexOf].setEntity?.(libraryItem) | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     shareClosed(mediaItemShare) { | ||||
|       if (this.entityName === 'items' || this.entityName === 'series-books') { | ||||
|         var indexOf = this.entities.findIndex((ent) => ent?.media?.id === mediaItemShare.mediaItemId) | ||||
|         if (indexOf >= 0) { | ||||
|           if (this.entityComponentRefs[indexOf]) { | ||||
|             const libraryItem = { ...this.entityComponentRefs[indexOf].libraryItem } | ||||
|             libraryItem.mediaItemShare = null | ||||
|             this.entityComponentRefs[indexOf].setEntity?.(libraryItem) | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     updatePagesLoaded() { | ||||
|       let numPages = Math.ceil(this.totalEntities / this.booksPerFetch) | ||||
|       for (let page = 0; page < numPages; page++) { | ||||
| @ -703,6 +727,8 @@ export default { | ||||
|         this.$root.socket.on('playlist_added', this.playlistAdded) | ||||
|         this.$root.socket.on('playlist_updated', this.playlistUpdated) | ||||
|         this.$root.socket.on('playlist_removed', this.playlistRemoved) | ||||
|         this.$root.socket.on('share_open', this.shareOpen) | ||||
|         this.$root.socket.on('share_closed', this.shareClosed) | ||||
|       } else { | ||||
|         console.error('Bookshelf - Socket not initialized') | ||||
|       } | ||||
| @ -730,6 +756,8 @@ export default { | ||||
|         this.$root.socket.off('playlist_added', this.playlistAdded) | ||||
|         this.$root.socket.off('playlist_updated', this.playlistUpdated) | ||||
|         this.$root.socket.off('playlist_removed', this.playlistRemoved) | ||||
|         this.$root.socket.off('share_open', this.shareOpen) | ||||
|         this.$root.socket.off('share_closed', this.shareClosed) | ||||
|       } else { | ||||
|         console.error('Bookshelf - Socket not initialized') | ||||
|       } | ||||
|  | ||||
| @ -528,6 +528,12 @@ export default { | ||||
|             func: 'openPlaylists', | ||||
|             text: this.$strings.LabelAddToPlaylist | ||||
|           }) | ||||
|           if (this.userIsAdminOrUp) { | ||||
|             items.push({ | ||||
|               func: 'openShare', | ||||
|               text: this.$strings.LabelShare | ||||
|             }) | ||||
|           } | ||||
|         } | ||||
|         if (this.ebookFormat && this.store.state.libraries.ereaderDevices?.length) { | ||||
|           items.push({ | ||||
| @ -880,6 +886,10 @@ export default { | ||||
|       this.store.commit('globals/setSelectedPlaylistItems', [{ libraryItem: this.libraryItem, episode: this.recentEpisode }]) | ||||
|       this.store.commit('globals/setShowPlaylistsModal', true) | ||||
|     }, | ||||
|     openShare() { | ||||
|       this.store.commit('setSelectedLibraryItem', this.libraryItem) | ||||
|       this.store.commit('globals/setShareModal', this.mediaItemShare) | ||||
|     }, | ||||
|     deleteLibraryItem() { | ||||
|       const payload = { | ||||
|         message: this.$strings.MessageConfirmDeleteLibraryItem, | ||||
|  | ||||
| @ -89,6 +89,9 @@ export default { | ||||
|         this.$emit('input', val) | ||||
|       } | ||||
|     }, | ||||
|     userIsAdminOrUp() { | ||||
|       return this.$store.getters['user/getIsAdminOrUp'] | ||||
|     }, | ||||
|     libraryMediaType() { | ||||
|       return this.$store.getters['libraries/getCurrentLibraryMediaType'] | ||||
|     }, | ||||
| @ -148,7 +151,7 @@ export default { | ||||
|       ] | ||||
|     }, | ||||
|     bookItems() { | ||||
|       return [ | ||||
|       const items = [ | ||||
|         { | ||||
|           text: this.$strings.LabelAll, | ||||
|           value: 'all' | ||||
| @ -229,13 +232,16 @@ export default { | ||||
|           text: this.$strings.LabelRSSFeedOpen, | ||||
|           value: 'feed-open', | ||||
|           sublist: false | ||||
|         }, | ||||
|         { | ||||
|         } | ||||
|       ] | ||||
|       if (this.userIsAdminOrUp) { | ||||
|         items.push({ | ||||
|           text: this.$strings.LabelShareOpen, | ||||
|           value: 'share-open', | ||||
|           sublist: false | ||||
|         } | ||||
|       ] | ||||
|         }) | ||||
|       } | ||||
|       return items | ||||
|     }, | ||||
|     podcastItems() { | ||||
|       return [ | ||||
|  | ||||
| @ -53,17 +53,7 @@ | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|   props: { | ||||
|     value: Boolean, | ||||
|     libraryItem: { | ||||
|       type: Object, | ||||
|       default: () => null | ||||
|     }, | ||||
|     mediaItemShare: { | ||||
|       type: Object, | ||||
|       default: () => null | ||||
|     } | ||||
|   }, | ||||
|   props: {}, | ||||
|   data() { | ||||
|     return { | ||||
|       processing: false, | ||||
| @ -99,12 +89,18 @@ export default { | ||||
|   computed: { | ||||
|     show: { | ||||
|       get() { | ||||
|         return this.value | ||||
|         return this.$store.state.globals.showShareModal | ||||
|       }, | ||||
|       set(val) { | ||||
|         this.$emit('input', val) | ||||
|         this.$store.commit('globals/setShowShareModal', val) | ||||
|       } | ||||
|     }, | ||||
|     mediaItemShare() { | ||||
|       return this.$store.state.globals.selectedMediaItemShare | ||||
|     }, | ||||
|     libraryItem() { | ||||
|       return this.$store.state.selectedLibraryItem | ||||
|     }, | ||||
|     user() { | ||||
|       return this.$store.state.user.user | ||||
|     }, | ||||
|  | ||||
| @ -20,6 +20,7 @@ | ||||
|     <modals-batch-quick-match-model /> | ||||
|     <modals-rssfeed-open-close-modal /> | ||||
|     <modals-raw-cover-preview-modal /> | ||||
|     <modals-share-modal /> | ||||
|     <prompt-confirm /> | ||||
|     <readers-reader /> | ||||
|   </div> | ||||
| @ -598,4 +599,4 @@ export default { | ||||
|     margin-left: 0px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| </style> | ||||
|  | ||||
| @ -147,7 +147,6 @@ | ||||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <modals-share-modal v-model="showShareModal" :media-item-share="mediaItemShare" :library-item="libraryItem" @opened="openedShare" @removed="removedShare" /> | ||||
|     <modals-podcast-episode-feed v-model="showPodcastEpisodeFeed" :library-item="libraryItem" :episodes="podcastFeedEpisodes" /> | ||||
|     <modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :library-item-id="libraryItemId" hide-create @select="selectBookmark" /> | ||||
|   </div> | ||||
| @ -186,8 +185,7 @@ export default { | ||||
|       episodeDownloadsQueued: [], | ||||
|       showBookmarksModal: false, | ||||
|       isDescriptionClamped: false, | ||||
|       showFullDescription: false, | ||||
|       showShareModal: false | ||||
|       showFullDescription: false | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
| @ -440,7 +438,7 @@ export default { | ||||
|         }) | ||||
|       } | ||||
| 
 | ||||
|       if (this.userIsAdminOrUp && !this.isPodcast) { | ||||
|       if (this.userIsAdminOrUp && !this.isPodcast && this.tracks.length) { | ||||
|         items.push({ | ||||
|           text: this.$strings.LabelShare, | ||||
|           action: 'share' | ||||
| @ -458,12 +456,6 @@ export default { | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     openedShare(mediaItemShare) { | ||||
|       this.mediaItemShare = mediaItemShare | ||||
|     }, | ||||
|     removedShare() { | ||||
|       this.mediaItemShare = null | ||||
|     }, | ||||
|     selectBookmark(bookmark) { | ||||
|       if (!bookmark) return | ||||
|       if (this.isStreaming) { | ||||
| @ -682,6 +674,16 @@ export default { | ||||
|         this.rssFeed = null | ||||
|       } | ||||
|     }, | ||||
|     shareOpen(mediaItemShare) { | ||||
|       if (mediaItemShare.mediaItemId === this.media.id) { | ||||
|         this.mediaItemShare = mediaItemShare | ||||
|       } | ||||
|     }, | ||||
|     shareClosed(mediaItemShare) { | ||||
|       if (mediaItemShare.mediaItemId === this.media.id) { | ||||
|         this.mediaItemShare = null | ||||
|       } | ||||
|     }, | ||||
|     queueBtnClick() { | ||||
|       if (this.isQueued) { | ||||
|         // Remove from queue | ||||
| @ -778,7 +780,8 @@ export default { | ||||
|       } else if (action === 'sendToDevice') { | ||||
|         this.sendToDevice(data) | ||||
|       } else if (action === 'share') { | ||||
|         this.showShareModal = true | ||||
|         this.$store.commit('setSelectedLibraryItem', this.libraryItem) | ||||
|         this.$store.commit('globals/setShareModal', this.mediaItemShare) | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
| @ -796,6 +799,8 @@ export default { | ||||
|     this.$root.socket.on('item_updated', this.libraryItemUpdated) | ||||
|     this.$root.socket.on('rss_feed_open', this.rssFeedOpen) | ||||
|     this.$root.socket.on('rss_feed_closed', this.rssFeedClosed) | ||||
|     this.$root.socket.on('share_open', this.shareOpen) | ||||
|     this.$root.socket.on('share_closed', this.shareClosed) | ||||
|     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) | ||||
| @ -805,6 +810,8 @@ export default { | ||||
|     this.$root.socket.off('item_updated', this.libraryItemUpdated) | ||||
|     this.$root.socket.off('rss_feed_open', this.rssFeedOpen) | ||||
|     this.$root.socket.off('rss_feed_closed', this.rssFeedClosed) | ||||
|     this.$root.socket.off('share_open', this.shareOpen) | ||||
|     this.$root.socket.off('share_closed', this.shareClosed) | ||||
|     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) | ||||
|  | ||||
| @ -10,6 +10,7 @@ export const state = () => ({ | ||||
|   showEditPodcastEpisode: false, | ||||
|   showViewPodcastEpisodeModal: false, | ||||
|   showRSSFeedOpenCloseModal: false, | ||||
|   showShareModal: false, | ||||
|   showConfirmPrompt: false, | ||||
|   showRawCoverPreviewModal: false, | ||||
|   confirmPromptOptions: null, | ||||
| @ -22,6 +23,7 @@ export const state = () => ({ | ||||
|   selectedAuthor: null, | ||||
|   selectedMediaItems: [], | ||||
|   selectedRawCoverUrl: null, | ||||
|   selectedMediaItemShare: null, | ||||
|   isCasting: false, // Actively casting
 | ||||
|   isChromecastInitialized: false, // Script loadeds
 | ||||
|   showBatchQuickMatchModal: false, | ||||
| @ -157,6 +159,13 @@ export const mutations = { | ||||
|     state.rssFeedEntity = entity | ||||
|     state.showRSSFeedOpenCloseModal = true | ||||
|   }, | ||||
|   setShowShareModal(state, val) { | ||||
|     state.showShareModal = val | ||||
|   }, | ||||
|   setShareModal(state, mediaItemShare) { | ||||
|     state.selectedMediaItemShare = mediaItemShare | ||||
|     state.showShareModal = true | ||||
|   }, | ||||
|   setShowConfirmPrompt(state, val) { | ||||
|     state.showConfirmPrompt = val | ||||
|   }, | ||||
|  | ||||
| @ -43,7 +43,7 @@ class LibraryItemController { | ||||
|         item.rssFeed = feedData?.toJSONMinified() || null | ||||
|       } | ||||
| 
 | ||||
|       if (item.mediaType === 'book' && includeEntities.includes('share')) { | ||||
|       if (item.mediaType === 'book' && req.user.isAdminOrUp && includeEntities.includes('share')) { | ||||
|         item.mediaItemShare = ShareManager.findByMediaItemId(item.media.id) | ||||
|       } | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| const Database = require('../Database') | ||||
| const Logger = require('../Logger') | ||||
| const SocketAuthority = require('../SocketAuthority') | ||||
| 
 | ||||
| /** | ||||
|  * @typedef OpenMediaItemShareObject | ||||
| @ -136,6 +137,7 @@ class ShareManager { | ||||
|     } else { | ||||
|       this.openMediaItemShares.push({ id: mediaItemShare.id, mediaItemShare: mediaItemShare.toJSON() }) | ||||
|     } | ||||
|     SocketAuthority.adminEmitter('share_open', mediaItemShare.toJSONForClient()) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
| @ -153,6 +155,12 @@ class ShareManager { | ||||
|     this.openMediaItemShares = this.openMediaItemShares.filter((s) => s.id !== mediaItemShareId) | ||||
|     this.openSharePlaybackSessions = this.openSharePlaybackSessions.filter((s) => s.mediaItemShareId !== mediaItemShareId) | ||||
|     await this.destroyMediaItemShare(mediaItemShareId) | ||||
| 
 | ||||
|     const mediaItemShareObjectForClient = { ...mediaItemShare.mediaItemShare } | ||||
|     delete mediaItemShareObjectForClient.pash | ||||
|     delete mediaItemShareObjectForClient.userId | ||||
|     delete mediaItemShareObjectForClient.extraData | ||||
|     SocketAuthority.adminEmitter('share_closed', mediaItemShareObjectForClient) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|  | ||||
| @ -567,8 +567,8 @@ class LibraryItem extends Model { | ||||
|         if (li.numEpisodesIncomplete) { | ||||
|           oldLibraryItem.numEpisodesIncomplete = li.numEpisodesIncomplete | ||||
|         } | ||||
|         if (li.mediaType === 'book' && options.include?.includes?.('share')) { | ||||
|           oldLibraryItem.mediaItemShare = ShareManager.findByMediaItemId(li.mediaId) | ||||
|         if (li.mediaItemShare) { | ||||
|           oldLibraryItem.mediaItemShare = li.mediaItemShare | ||||
|         } | ||||
| 
 | ||||
|         return oldLibraryItem | ||||
|  | ||||
| @ -332,9 +332,9 @@ module.exports = { | ||||
|   /** | ||||
|    * Get library items for book media type using filter and sort | ||||
|    * @param {string} libraryId | ||||
|    * @param {[oldUser]} user | ||||
|    * @param {[string]} filterGroup | ||||
|    * @param {[string]} filterValue | ||||
|    * @param {import('../../objects/user/User')} user | ||||
|    * @param {string|null} filterGroup | ||||
|    * @param {string|null} filterValue | ||||
|    * @param {string} sortBy | ||||
|    * @param {string} sortDesc | ||||
|    * @param {boolean} collapseseries | ||||
| @ -356,7 +356,7 @@ module.exports = { | ||||
|       sortBy = 'media.metadata.title' | ||||
|     } | ||||
|     const includeRSSFeed = include.includes('rssfeed') | ||||
|     const includeMediaItemShare = include.includes('share') | ||||
|     const includeMediaItemShare = !!user?.isAdminOrUp && include.includes('share') | ||||
| 
 | ||||
|     // For sorting by author name an additional attribute must be added
 | ||||
|     //   with author names concatenated
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user