mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Add: More menu on book card #173, Fix: Download worker ffmpeg logs
This commit is contained in:
		
							parent
							
								
									7141f70aa5
								
							
						
					
					
						commit
						54570a3b27
					
				| @ -1,5 +1,5 @@ | ||||
| <template> | ||||
|   <div class="relative"> | ||||
|   <div ref="wrapper" class="relative"> | ||||
|     <!-- New Book Flag --> | ||||
|     <div v-show="isNew" class="absolute top-4 left-0 w-4 h-10 pr-2 bg-darkgreen box-shadow-xl z-20"> | ||||
|       <div class="absolute top-0 left-0 w-full h-full transform -rotate-90 flex items-center justify-center"> | ||||
| @ -8,13 +8,13 @@ | ||||
|       <div class="absolute -bottom-4 left-0 triangle-right" /> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="rounded-sm h-full overflow-hidden relative" :style="{ padding: `${paddingY}px ${paddingX}px` }" @click.stop> | ||||
|     <div class="rounded-sm h-full overflow-hidden relative" :style="{ padding: `${paddingY}px ${paddingX}px` }"> | ||||
|       <nuxt-link :to="isSelectionMode ? '' : `/audiobook/${audiobookId}`" class="cursor-pointer"> | ||||
|         <div class="w-full relative box-shadow-book" :style="{ height: height + 'px' }" @click="clickCard" @mouseover="isHovering = true" @mouseleave="isHovering = false"> | ||||
|           <covers-book-cover :audiobook="audiobook" :author-override="authorFormat" :width="width" /> | ||||
| 
 | ||||
|           <!-- Hidden SM and DOWN --> | ||||
|           <div v-show="isHovering || isSelectionMode" class="absolute top-0 left-0 w-full h-full bg-black rounded hidden md:block" :class="overlayWrapperClasslist"> | ||||
|           <div v-show="isHovering || isSelectionMode || isMoreMenuOpen" class="absolute top-0 left-0 w-full h-full bg-black rounded hidden md:block" :class="overlayWrapperClasslist"> | ||||
|             <div v-show="showPlayButton" class="h-full flex items-center justify-center"> | ||||
|               <div class="hover:text-gray-200 hover:scale-110 transform duration-200" @click.stop.prevent="play"> | ||||
|                 <span class="material-icons" :style="{ fontSize: playIconFontSize + 'rem' }">play_circle_filled</span> | ||||
| @ -33,6 +33,11 @@ | ||||
|             <div class="absolute cursor-pointer hover:text-yellow-300 hover:scale-125 transform duration-100" :style="{ top: 0.375 * sizeMultiplier + 'rem', left: 0.375 * sizeMultiplier + 'rem' }" @click.stop.prevent="selectBtnClick"> | ||||
|               <span class="material-icons" :class="selected ? 'text-yellow-400' : ''" :style="{ fontSize: 1.25 * sizeMultiplier + 'rem' }">{{ selected ? 'radio_button_checked' : 'radio_button_unchecked' }}</span> | ||||
|             </div> | ||||
| 
 | ||||
|             <!-- More Icon --> | ||||
|             <div ref="moreIcon" v-show="!isSelectionMode" class="hidden md:block absolute cursor-pointer hover:text-yellow-300" :style="{ bottom: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem' }" @click.stop.prevent="clickShowMore"> | ||||
|               <span class="material-icons" :style="{ fontSize: 1.2 * sizeMultiplier + 'rem' }">more_vert</span> | ||||
|             </div> | ||||
|           </div> | ||||
| 
 | ||||
|           <div v-if="volumeNumber && showVolumeNumber && !isHovering && !isSelectionMode" class="absolute rounded-lg bg-black bg-opacity-90 box-shadow-md" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem', padding: `${0.1 * sizeMultiplier}rem ${0.25 * sizeMultiplier}rem` }"> | ||||
| @ -43,7 +48,7 @@ | ||||
|           <div | ||||
|             v-if="showSmallEBookIcon" | ||||
|             class="absolute rounded-full bg-blue-500 flex items-center justify-center bg-opacity-90 hover:scale-125 transform duration-200" | ||||
|             :style="{ bottom: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem', padding: `${0.1 * sizeMultiplier}rem ${0.25 * sizeMultiplier}rem`, width: 1.5 * sizeMultiplier + 'rem', height: 1.5 * sizeMultiplier + 'rem' }" | ||||
|             :style="{ bottom: 0.375 * sizeMultiplier + 'rem', left: 0.375 * sizeMultiplier + 'rem', padding: `${0.1 * sizeMultiplier}rem ${0.25 * sizeMultiplier}rem`, width: 1.5 * sizeMultiplier + 'rem', height: 1.5 * sizeMultiplier + 'rem' }" | ||||
|             @click.stop.prevent="clickReadEBook" | ||||
|           > | ||||
|             <!-- <p :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }">EBook</p> --> | ||||
| @ -64,6 +69,9 @@ | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| import Vue from 'vue' | ||||
| import MoreMenu from '@/components/widgets/MoreMenu' | ||||
| 
 | ||||
| export default { | ||||
|   props: { | ||||
|     audiobook: { | ||||
| @ -86,7 +94,10 @@ export default { | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       isHovering: false | ||||
|       isHovering: false, | ||||
|       isMoreMenuOpen: false, | ||||
|       isProcessingReadUpdate: false, | ||||
|       rescanning: false | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
| @ -220,6 +231,46 @@ export default { | ||||
|     }, | ||||
|     userCanDelete() { | ||||
|       return this.$store.getters['user/getUserCanDelete'] | ||||
|     }, | ||||
|     userCanDownload() { | ||||
|       return this.$store.getters['user/getUserCanDownload'] | ||||
|     }, | ||||
|     userIsRoot() { | ||||
|       return this.$store.getters['user/getIsRoot'] | ||||
|     }, | ||||
|     moreMenuItems() { | ||||
|       var items = [ | ||||
|         { | ||||
|           func: 'toggleRead', | ||||
|           text: `Mark as ${this.userIsRead ? 'Not Read' : 'Read'}` | ||||
|         } | ||||
|       ] | ||||
|       if (this.userCanUpdate) { | ||||
|         if (this.hasTracks) { | ||||
|           items.push({ | ||||
|             func: 'showEditModalTracks', | ||||
|             text: 'Tracks' | ||||
|           }) | ||||
|         } | ||||
| 
 | ||||
|         items.push({ | ||||
|           func: 'showEditModalMatch', | ||||
|           text: 'Match' | ||||
|         }) | ||||
|       } | ||||
|       if (this.userCanDownload) { | ||||
|         items.push({ | ||||
|           func: 'showEditModalDownload', | ||||
|           text: 'Download' | ||||
|         }) | ||||
|       } | ||||
|       if (this.userIsRoot) { | ||||
|         items.push({ | ||||
|           func: 'rescan', | ||||
|           text: 'Re-Scan' | ||||
|         }) | ||||
|       } | ||||
|       return items | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
| @ -248,6 +299,103 @@ export default { | ||||
|     }, | ||||
|     clickReadEBook() { | ||||
|       this.$store.commit('showEReader', this.audiobook) | ||||
|     }, | ||||
|     toggleRead() { | ||||
|       // More menu func | ||||
|       var updatePayload = { | ||||
|         isRead: !this.userIsRead | ||||
|       } | ||||
|       this.isProcessingReadUpdate = true | ||||
|       this.$axios | ||||
|         .$patch(`/api/user/audiobook/${this.audiobookId}`, updatePayload) | ||||
|         .then(() => { | ||||
|           this.isProcessingReadUpdate = false | ||||
|           this.$toast.success(`"${this.title}" Marked as ${updatePayload.isRead ? 'Read' : 'Not Read'}`) | ||||
|         }) | ||||
|         .catch((error) => { | ||||
|           console.error('Failed', error) | ||||
|           this.isProcessingReadUpdate = false | ||||
|           this.$toast.error(`Failed to mark as ${updatePayload.isRead ? 'Read' : 'Not Read'}`) | ||||
|         }) | ||||
|     }, | ||||
|     audiobookScanComplete(result) { | ||||
|       this.rescanning = false | ||||
|       if (!result) { | ||||
|         this.$toast.error(`Re-Scan Failed for "${this.title}"`) | ||||
|       } else if (result === 'UPDATED') { | ||||
|         this.$toast.success(`Re-Scan complete audiobook was updated`) | ||||
|       } else if (result === 'UPTODATE') { | ||||
|         this.$toast.success(`Re-Scan complete audiobook was up to date`) | ||||
|       } else if (result === 'REMOVED') { | ||||
|         this.$toast.error(`Re-Scan complete audiobook was removed`) | ||||
|       } | ||||
|     }, | ||||
|     rescan() { | ||||
|       this.rescanning = true | ||||
|       this.$root.socket.once('audiobook_scan_complete', this.audiobookScanComplete) | ||||
|       this.$root.socket.emit('scan_audiobook', this.audiobookId) | ||||
|     }, | ||||
|     showEditModalTracks() { | ||||
|       // More menu func | ||||
|       this.$store.commit('showEditModalOnTab', { audiobook: this.audiobook, tab: 'tracks' }) | ||||
|     }, | ||||
|     showEditModalMatch() { | ||||
|       // More menu func | ||||
|       this.$store.commit('showEditModalOnTab', { audiobook: this.audiobook, tab: 'match' }) | ||||
|     }, | ||||
|     showEditModalDownload() { | ||||
|       // More menu func | ||||
|       this.$store.commit('showEditModalOnTab', { audiobook: this.audiobook, tab: 'download' }) | ||||
|     }, | ||||
|     createMoreMenu() { | ||||
|       if (!this.$refs.moreIcon) return | ||||
| 
 | ||||
|       var ComponentClass = Vue.extend(MoreMenu) | ||||
| 
 | ||||
|       var _this = this | ||||
|       var instance = new ComponentClass({ | ||||
|         propsData: { | ||||
|           items: this.moreMenuItems | ||||
|         }, | ||||
|         created() { | ||||
|           this.$on('action', (func) => { | ||||
|             if (_this[func]) _this[func]() | ||||
|           }) | ||||
|           this.$on('close', () => { | ||||
|             _this.isMoreMenuOpen = false | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|       instance.$mount() | ||||
| 
 | ||||
|       var wrapperBox = this.$refs.moreIcon.getBoundingClientRect() | ||||
|       var el = instance.$el | ||||
| 
 | ||||
|       var elHeight = this.moreMenuItems.length * 28 + 2 | ||||
|       var elWidth = 130 | ||||
| 
 | ||||
|       var bottomOfIcon = wrapperBox.top + wrapperBox.height | ||||
|       var rightOfIcon = wrapperBox.left + wrapperBox.width | ||||
| 
 | ||||
|       var elTop = bottomOfIcon | ||||
|       var elLeft = rightOfIcon | ||||
|       if (bottomOfIcon + elHeight > window.innerHeight - 100) { | ||||
|         elTop = wrapperBox.top - elHeight | ||||
|         elLeft = wrapperBox.left | ||||
|       } | ||||
| 
 | ||||
|       if (rightOfIcon + elWidth > window.innerWidth - 100) { | ||||
|         elLeft = rightOfIcon - elWidth | ||||
|       } | ||||
| 
 | ||||
|       el.style.top = elTop + 'px' | ||||
|       el.style.left = elLeft + 'px' | ||||
| 
 | ||||
|       this.isMoreMenuOpen = true | ||||
|       document.body.appendChild(el) | ||||
|     }, | ||||
|     clickShowMore() { | ||||
|       this.createMoreMenu() | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -132,7 +132,7 @@ export default { | ||||
|         if (tab.id === 'download' && this.isMissing) return false | ||||
|         if ((tab.id === 'download' || tab.id === 'tracks') && this.userCanDownload) return true | ||||
|         if (tab.id !== 'download' && tab.id !== 'tracks' && this.userCanUpdate) return true | ||||
|         if (tab.id === 'match' && this.showExperimentalFeatures) return true | ||||
|         if (tab.id === 'match' && this.userCanUpdate && this.showExperimentalFeatures) return true | ||||
|         return false | ||||
|       }) | ||||
|     }, | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <template> | ||||
|   <div class="relative w-full" v-click-outside="clickOutside"> | ||||
|   <div class="relative w-full" v-click-outside="clickOutsideObj"> | ||||
|     <p class="text-sm font-semibold">{{ label }}</p> | ||||
|     <button type="button" :disabled="disabled" class="relative w-full border border-gray-500 rounded shadow-sm pl-3 pr-8 py-2 text-left focus:outline-none sm:text-sm cursor-pointer bg-primary" :class="small ? 'h-9' : 'h-10'" aria-haspopup="listbox" aria-expanded="true" @click.stop.prevent="clickShowMenu"> | ||||
|       <span class="flex items-center"> | ||||
| @ -41,6 +41,11 @@ export default { | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       clickOutsideObj: { | ||||
|         handler: this.clickedOutside, | ||||
|         events: ['mousedown'], | ||||
|         isActive: true | ||||
|       }, | ||||
|       showMenu: false | ||||
|     } | ||||
|   }, | ||||
| @ -65,7 +70,7 @@ export default { | ||||
|       if (this.disabled) return | ||||
|       this.showMenu = !this.showMenu | ||||
|     }, | ||||
|     clickOutside() { | ||||
|     clickedOutside() { | ||||
|       this.showMenu = false | ||||
|     }, | ||||
|     clickedOption(itemValue) { | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| <template> | ||||
|   <div v-if="currentLibrary" class="relative w-36 h-8" v-click-outside="clickOutside"> | ||||
|   <div v-if="currentLibrary" class="relative w-36 h-8" v-click-outside="clickOutsideObj"> | ||||
|     <button type="button" :disabled="disabled" class="relative h-full w-full border border-white border-opacity-10 hover:border-opacity-20 rounded shadow-sm pl-3 pr-3 text-left focus:outline-none cursor-pointer bg-black bg-opacity-20 text-gray-400 hover:text-gray-200" aria-haspopup="listbox" aria-expanded="true" @click.stop.prevent="clickShowMenu"> | ||||
|       <span class="flex items-center"> | ||||
|         <widgets-library-icon :icon="currentLibraryIcon" class="mr-2" /> | ||||
| @ -26,6 +26,11 @@ | ||||
| export default { | ||||
|   data() { | ||||
|     return { | ||||
|       clickOutsideObj: { | ||||
|         handler: this.clickedOutside, | ||||
|         events: ['mousedown'], | ||||
|         isActive: true | ||||
|       }, | ||||
|       showMenu: false, | ||||
|       disabled: false | ||||
|     } | ||||
| @ -61,7 +66,7 @@ export default { | ||||
|       if (this.disabled) return | ||||
|       this.showMenu = !this.showMenu | ||||
|     }, | ||||
|     clickOutside() { | ||||
|     clickedOutside() { | ||||
|       this.showMenu = false | ||||
|     }, | ||||
|     selectLibrary(library) { | ||||
|  | ||||
							
								
								
									
										50
									
								
								client/components/widgets/MoreMenu.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								client/components/widgets/MoreMenu.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| <template> | ||||
|   <div class="absolute w-32 bg-bg rounded-md border border-black-200 shadow-lg z-50" v-click-outside="clickOutsideObj" style="top: 0; left: 0"> | ||||
|     <template v-for="(item, index) in items"> | ||||
|       <div :key="index" class="flex h-7 items-center px-2 hover:bg-white hover:bg-opacity-5 text-white text-xs cursor-pointer" @click="clickAction(item.func)"> | ||||
|         <p>{{ item.text }}</p> | ||||
|       </div> | ||||
|     </template> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| <script> | ||||
| export default { | ||||
|   props: { | ||||
|     items: { | ||||
|       type: Array, | ||||
|       default: () => [] | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       clickOutsideObj: { | ||||
|         handler: this.clickedOutside, | ||||
|         events: ['mousedown'], | ||||
|         isActive: true | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   computed: {}, | ||||
|   methods: { | ||||
|     clickAction(func) { | ||||
|       this.$emit('action', func) | ||||
|       this.close() | ||||
|     }, | ||||
|     clickedOutside(e) { | ||||
|       this.close() | ||||
|     }, | ||||
|     close() { | ||||
|       this.$emit('close') | ||||
| 
 | ||||
|       // destroy the vue listeners, etc | ||||
|       this.$destroy() | ||||
| 
 | ||||
|       // remove the element from the DOM | ||||
|       this.$el.parentNode.removeChild(this.$el) | ||||
|     } | ||||
|   }, | ||||
|   mounted() {}, | ||||
|   beforeDestroy() {} | ||||
| } | ||||
| </script> | ||||
							
								
								
									
										7
									
								
								client/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								client/package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "audiobookshelf-client", | ||||
|   "version": "1.4.10", | ||||
|   "version": "1.6.13", | ||||
|   "lockfileVersion": 1, | ||||
|   "requires": true, | ||||
|   "dependencies": { | ||||
| @ -13298,6 +13298,11 @@ | ||||
|       "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", | ||||
|       "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" | ||||
|     }, | ||||
|     "v-click-outside": { | ||||
|       "version": "3.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/v-click-outside/-/v-click-outside-3.1.2.tgz", | ||||
|       "integrity": "sha512-gMdRqfRE6m6XU6SiFi3dyBlFB2MWogiXpof8Aa3LQysrl9pzTndqp/iEaAphLoadaQUFnQ0ec6fLLaxr7LiY6A==" | ||||
|     }, | ||||
|     "vary": { | ||||
|       "version": "1.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", | ||||
|  | ||||
| @ -22,6 +22,7 @@ | ||||
|     "libarchive.js": "^1.3.0", | ||||
|     "nuxt": "^2.15.7", | ||||
|     "nuxt-socket-io": "^1.1.18", | ||||
|     "v-click-outside": "^3.1.2", | ||||
|     "vue-pdf": "^4.3.0", | ||||
|     "vue-toastification": "^1.7.11", | ||||
|     "vuedraggable": "^2.24.3" | ||||
| @ -30,4 +31,4 @@ | ||||
|     "@nuxtjs/tailwindcss": "^4.2.1", | ||||
|     "postcss": "^8.3.6" | ||||
|   } | ||||
| } | ||||
| } | ||||
|  | ||||
| @ -1,6 +1,9 @@ | ||||
| import Vue from 'vue' | ||||
| import vClickOutside from 'v-click-outside' | ||||
| import { formatDistance, format } from 'date-fns' | ||||
| 
 | ||||
| Vue.directive('click-outside', vClickOutside.directive) | ||||
| 
 | ||||
| Vue.prototype.$eventBus = new Vue() | ||||
| 
 | ||||
| Vue.prototype.$isDev = process.env.NODE_ENV !== 'production' | ||||
| @ -73,42 +76,41 @@ Vue.prototype.$calculateTextSize = (text, styles = {}) => { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function isClickedOutsideEl(clickEvent, elToCheckOutside, ignoreSelectors = [], ignoreElems = []) { | ||||
|   const isDOMElement = (element) => { | ||||
|     return element instanceof Element || element instanceof HTMLDocument | ||||
|   } | ||||
| // function isClickedOutsideEl(clickEvent, elToCheckOutside, ignoreSelectors = [], ignoreElems = []) {
 | ||||
| //   const isDOMElement = (element) => {
 | ||||
| //     return element instanceof Element || element instanceof HTMLDocument
 | ||||
| //   }
 | ||||
| 
 | ||||
|   const clickedEl = clickEvent.srcElement | ||||
|   const didClickOnIgnoredEl = ignoreElems.filter((el) => el).some((element) => element.contains(clickedEl) || element.isEqualNode(clickedEl)) | ||||
|   const didClickOnIgnoredSelector = ignoreSelectors.length ? ignoreSelectors.map((selector) => clickedEl.closest(selector)).reduce((curr, accumulator) => curr && accumulator, true) : false | ||||
| //   const clickedEl = clickEvent.srcElement
 | ||||
| //   const didClickOnIgnoredEl = ignoreElems.filter((el) => el).some((element) => element.contains(clickedEl) || element.isEqualNode(clickedEl))
 | ||||
| //   const didClickOnIgnoredSelector = ignoreSelectors.length ? ignoreSelectors.map((selector) => clickedEl.closest(selector)).reduce((curr, accumulator) => curr && accumulator, true) : false
 | ||||
| 
 | ||||
|   if (isDOMElement(elToCheckOutside) && !elToCheckOutside.contains(clickedEl) && !didClickOnIgnoredEl && !didClickOnIgnoredSelector) { | ||||
|     return true | ||||
|   } | ||||
| //   if (isDOMElement(elToCheckOutside) && !elToCheckOutside.contains(clickedEl) && !didClickOnIgnoredEl && !didClickOnIgnoredSelector) {
 | ||||
| //     return true
 | ||||
| //   }
 | ||||
| //   return false
 | ||||
| // }
 | ||||
| 
 | ||||
|   return false | ||||
| } | ||||
| 
 | ||||
| Vue.directive('click-outside', { | ||||
|   bind: function (el, binding, vnode) { | ||||
|     let vm = vnode.context; | ||||
|     let callback = binding.value; | ||||
|     if (typeof callback !== 'function') { | ||||
|       console.error('Invalid callback', binding) | ||||
|       return | ||||
|     } | ||||
|     el['__click_outside__'] = (ev) => { | ||||
|       if (isClickedOutsideEl(ev, el)) { | ||||
|         callback.call(vm, ev) | ||||
|       } | ||||
|     } | ||||
|     document.addEventListener('click', el['__click_outside__'], false) | ||||
|   }, | ||||
|   unbind: function (el, binding, vnode) { | ||||
|     document.removeEventListener('click', el['__click_outside__'], false) | ||||
|     delete el['__click_outside__'] | ||||
|   } | ||||
| }) | ||||
| // Vue.directive('click-outside', {
 | ||||
| //   bind: function (el, binding, vnode) {
 | ||||
| //     let vm = vnode.context;
 | ||||
| //     let callback = binding.value;
 | ||||
| //     if (typeof callback !== 'function') {
 | ||||
| //       console.error('Invalid callback', binding)
 | ||||
| //       return
 | ||||
| //     }
 | ||||
| //     el['__click_outside__'] = (ev) => {
 | ||||
| //       if (isClickedOutsideEl(ev, el)) {
 | ||||
| //         callback.call(vm, ev)
 | ||||
| //       }
 | ||||
| //     }
 | ||||
| //     document.addEventListener('click', el['__click_outside__'], false)
 | ||||
| //   },
 | ||||
| //   unbind: function (el, binding, vnode) {
 | ||||
| //     document.removeEventListener('click', el['__click_outside__'], false)
 | ||||
| //     delete el['__click_outside__']
 | ||||
| //   }
 | ||||
| // })
 | ||||
| 
 | ||||
| Vue.prototype.$sanitizeFilename = (input, replacement = '') => { | ||||
|   if (typeof input !== 'string') { | ||||
|  | ||||
| @ -285,6 +285,10 @@ class DownloadManager { | ||||
|           if (!download.isTimedOut) { | ||||
|             this.sendResult(download, message) | ||||
|           } | ||||
|         } else if (message.type === 'FFMPEG') { | ||||
|           if (Logger[message.level]) { | ||||
|             Logger[message.level](message.log) | ||||
|           } | ||||
|         } | ||||
|       } else { | ||||
|         Logger.error('Invalid worker message', message) | ||||
|  | ||||
| @ -5,10 +5,12 @@ if (process.env.FFMPEG_PATH) { | ||||
| } | ||||
| 
 | ||||
| const { parentPort, workerData } = require("worker_threads") | ||||
| const Logger = require('../Logger') | ||||
| 
 | ||||
| Logger.info('[DownloadWorker] Starting Worker...') | ||||
| 
 | ||||
| parentPort.postMessage({ | ||||
|   type: 'FFMPEG', | ||||
|   level: 'debug', | ||||
|   log: '[DownloadWorker] Starting Worker...' | ||||
| }) | ||||
| 
 | ||||
| const ffmpegCommand = Ffmpeg() | ||||
| const startTime = Date.now() | ||||
| @ -27,25 +29,45 @@ var isKilled = false | ||||
| async function runFfmpeg() { | ||||
|   var success = await new Promise((resolve) => { | ||||
|     ffmpegCommand.on('start', (command) => { | ||||
|       Logger.info('[DownloadWorker] FFMPEG concat started with command: ' + command) | ||||
|       parentPort.postMessage({ | ||||
|         type: 'FFMPEG', | ||||
|         level: 'info', | ||||
|         log: '[DownloadWorker] FFMPEG concat started with command: ' + command | ||||
|       }) | ||||
|     }) | ||||
| 
 | ||||
|     ffmpegCommand.on('stderr', (stdErrline) => { | ||||
|       Logger.info(stdErrline) | ||||
|       parentPort.postMessage({ | ||||
|         type: 'FFMPEG', | ||||
|         level: 'error', | ||||
|         log: '[DownloadWorker] Ffmpeg Stderr: ' + stdErrline | ||||
|       }) | ||||
|     }) | ||||
| 
 | ||||
|     ffmpegCommand.on('error', (err, stdout, stderr) => { | ||||
|       if (err.message && err.message.includes('SIGKILL')) { | ||||
|         // This is an intentional SIGKILL
 | ||||
|         Logger.info('[DownloadWorker] User Killed singleAudio') | ||||
|         parentPort.postMessage({ | ||||
|           type: 'FFMPEG', | ||||
|           level: 'info', | ||||
|           log: '[DownloadWorker] User Killed worker' | ||||
|         }) | ||||
|       } else { | ||||
|         Logger.error('[DownloadWorker] Ffmpeg Err', err.message) | ||||
|         parentPort.postMessage({ | ||||
|           type: 'FFMPEG', | ||||
|           level: 'error', | ||||
|           log: '[DownloadWorker] Ffmpeg Err: ' + err.message | ||||
|         }) | ||||
|       } | ||||
|       resolve(false) | ||||
|     }) | ||||
| 
 | ||||
|     ffmpegCommand.on('end', (stdout, stderr) => { | ||||
|       Logger.info('[DownloadWorker] singleAudio ended') | ||||
|       parentPort.postMessage({ | ||||
|         type: 'FFMPEG', | ||||
|         level: 'info', | ||||
|         log: '[DownloadWorker] worker ended' | ||||
|       }) | ||||
|       resolve(true) | ||||
|     }) | ||||
|     ffmpegCommand.run() | ||||
| @ -62,7 +84,6 @@ async function runFfmpeg() { | ||||
| 
 | ||||
| parentPort.on('message', (message) => { | ||||
|   if (message === 'STOP') { | ||||
|     Logger.info('[DownloadWorker] Requested a hard stop') | ||||
|     isKilled = true | ||||
|     ffmpegCommand.kill() | ||||
|   } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user