mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Merge branch 'master' into plugin-implementation-demo
This commit is contained in:
		
						commit
						c60d74774a
					
				| @ -1,8 +1,8 @@ | ||||
| <template> | ||||
|   <div ref="wrapper" class="hidden absolute top-0 left-0 w-full h-full bg-black bg-opacity-50 rounded-lg items-center justify-center" style="z-index: 61" @click="clickClose"> | ||||
|     <div class="absolute top-3 right-3 md:top-5 md:right-5 h-8 w-8 md:h-12 md:w-12 flex items-center justify-center cursor-pointer text-white hover:text-gray-300"> | ||||
|   <div ref="wrapper" role="dialog" aria-modal="true" class="hidden absolute top-0 left-0 w-full h-full bg-black bg-opacity-50 rounded-lg items-center justify-center" style="z-index: 61" @click="clickClose"> | ||||
|     <button type="button" class="absolute top-3 right-3 md:top-5 md:right-5 h-8 w-8 md:h-12 md:w-12 flex items-center justify-center cursor-pointer text-white hover:text-gray-300" aria-label="Close modal"> | ||||
|       <span class="material-symbols text-2xl md:text-4xl">close</span> | ||||
|     </div> | ||||
|     </button> | ||||
|     <div ref="content" class="text-white"> | ||||
|       <form v-if="selectedSeries" @submit.prevent="submitSeriesForm"> | ||||
|         <div class="bg-bg rounded-lg px-2 py-6 sm:p-6 md:p-8" @click.stop> | ||||
|  | ||||
| @ -2,24 +2,24 @@ | ||||
|   <modals-modal v-model="show" name="edit-book" :width="800" :height="height" :processing="processing" :content-margin-top="marginTop"> | ||||
|     <template #outer> | ||||
|       <div class="absolute top-0 left-0 p-4 landscape:px-4 landscape:py-2 md:portrait:p-5 lg:p-5 w-2/3 overflow-hidden pointer-events-none"> | ||||
|         <p class="text-xl md:portrait:text-3xl md:landscape:text-lg lg:text-3xl text-white truncate pointer-events-none">{{ title }}</p> | ||||
|         <h1 class="text-xl md:portrait:text-3xl md:landscape:text-lg lg:text-3xl text-white truncate pointer-events-none">{{ title }}</h1> | ||||
|       </div> | ||||
|     </template> | ||||
|     <div class="absolute -top-10 left-0 z-10 w-full flex"> | ||||
|     <div role="tablist" class="absolute -top-10 left-0 z-10 w-full flex"> | ||||
|       <template v-for="tab in availableTabs"> | ||||
|         <div :key="tab.id" class="w-28 rounded-t-lg flex items-center justify-center mr-0.5 sm:mr-1 cursor-pointer hover:bg-bg border-t border-l border-r border-black-300 tab text-xs sm:text-base" :class="selectedTab === tab.id ? 'tab-selected bg-bg pb-px' : 'bg-primary text-gray-400'" @click="selectTab(tab.id)">{{ tab.title }}</div> | ||||
|         <button :key="tab.id" role="tab" class="w-28 rounded-t-lg flex items-center justify-center mr-0.5 sm:mr-1 cursor-pointer hover:bg-bg border-t border-l border-r border-black-300 tab text-xs sm:text-base" :class="selectedTab === tab.id ? 'tab-selected bg-bg pb-px' : 'bg-primary text-gray-400'" @click="selectTab(tab.id)">{{ tab.title }}</button> | ||||
|       </template> | ||||
|     </div> | ||||
| 
 | ||||
|     <div v-show="canGoPrev" class="absolute -left-24 top-0 bottom-0 h-full pointer-events-none flex items-center px-6"> | ||||
|       <div class="material-symbols text-5xl text-white text-opacity-50 hover:text-opacity-90 cursor-pointer pointer-events-auto" @click.stop.prevent="goPrevBook" @mousedown.prevent>arrow_back_ios</div> | ||||
|     </div> | ||||
|     <div v-show="canGoNext" class="absolute -right-24 top-0 bottom-0 h-full pointer-events-none flex items-center px-6"> | ||||
|       <div class="material-symbols text-5xl text-white text-opacity-50 hover:text-opacity-90 cursor-pointer pointer-events-auto" @click.stop.prevent="goNextBook" @mousedown.prevent>arrow_forward_ios</div> | ||||
|     <div role="tabpanel" class="w-full h-full max-h-full text-sm rounded-b-lg rounded-tr-lg bg-bg shadow-lg border border-black-300 relative"> | ||||
|       <component v-if="libraryItem && show" :is="tabName" :library-item="libraryItem" :processing.sync="processing" @close="show = false" @selectTab="selectTab" /> | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="w-full h-full max-h-full text-sm rounded-b-lg rounded-tr-lg bg-bg shadow-lg border border-black-300 relative"> | ||||
|       <component v-if="libraryItem && show" :is="tabName" :library-item="libraryItem" :processing.sync="processing" @close="show = false" @selectTab="selectTab" /> | ||||
|     <div v-show="canGoPrev" class="absolute -left-24 top-0 bottom-0 h-full pointer-events-none flex items-center px-6"> | ||||
|       <button class="material-symbols text-5xl text-white text-opacity-50 hover:text-opacity-90 cursor-pointer pointer-events-auto" :aria-label="$strings.ButtonNext" @click.stop.prevent="goPrevBook" @mousedown.prevent>arrow_back_ios</button> | ||||
|     </div> | ||||
|     <div v-show="canGoNext" class="absolute -right-24 top-0 bottom-0 h-full pointer-events-none flex items-center px-6"> | ||||
|       <button class="material-symbols text-5xl text-white text-opacity-50 hover:text-opacity-90 cursor-pointer pointer-events-auto" :aria-label="$strings.ButtonPrevious" @click.stop.prevent="goNextBook" @mousedown.prevent>arrow_forward_ios</button> | ||||
|     </div> | ||||
|   </modals-modal> | ||||
| </template> | ||||
|  | ||||
| @ -1,17 +1,17 @@ | ||||
| <template> | ||||
|   <div class="w-full"> | ||||
|     <p class="px-1 text-sm font-semibold" :class="disabled ? 'text-gray-400' : ''">{{ label }}</p> | ||||
|     <label :for="identifier" class="px-1 text-sm font-semibold" :class="disabled ? 'text-gray-400' : ''">{{ label }}</label> | ||||
|     <div ref="wrapper" class="relative"> | ||||
|       <form @submit.prevent="submitForm"> | ||||
|         <div ref="inputWrapper" style="min-height: 36px" class="flex-wrap relative w-full shadow-sm flex items-center border border-gray-600 rounded px-2 py-1" :class="wrapperClass" @click.stop.prevent="clickWrapper" @mouseup.stop.prevent @mousedown.prevent> | ||||
|           <div v-for="item in selected" :key="item" class="rounded-full px-2 py-1 mx-0.5 my-0.5 text-xs bg-bg flex flex-nowrap break-all items-center relative"> | ||||
|             <div v-if="!disabled" class="w-full h-full rounded-full absolute top-0 left-0 px-1 bg-bg bg-opacity-75 flex items-center justify-end opacity-0 hover:opacity-100"> | ||||
|               <span v-if="showEdit" class="material-symbols text-white hover:text-warning cursor-pointer" style="font-size: 1.1rem" @click.stop="editItem(item)">edit</span> | ||||
|               <span class="material-symbols text-white hover:text-error cursor-pointer" style="font-size: 1.1rem" @click.stop="removeItem(item)">close</span> | ||||
|         <div ref="inputWrapper" role="list" style="min-height: 36px" class="flex-wrap relative w-full shadow-sm flex items-center border border-gray-600 rounded px-2 py-1" :class="wrapperClass" @click.stop.prevent="clickWrapper" @mouseup.stop.prevent @mousedown.prevent> | ||||
|           <div v-for="item in selected" :key="item" role="listitem" class="rounded-full px-2 py-1 mx-0.5 my-0.5 text-xs bg-bg flex flex-nowrap break-all items-center relative"> | ||||
|             <div v-if="!disabled" class="w-full h-full rounded-full absolute top-0 left-0 px-1 bg-bg bg-opacity-75 flex items-center justify-end opacity-0 hover:opacity-100" :class="{ 'opacity-100': inputFocused }"> | ||||
|               <button v-if="showEdit" type="button" :aria-label="$strings.ButtonEdit" class="material-symbols text-white hover:text-warning cursor-pointer" style="font-size: 1.1rem" @click.stop="editItem(item)">edit</button> | ||||
|               <button type="button" :aria-label="$strings.ButtonRemove" class="material-symbols text-white hover:text-error focus:text-error cursor-pointer" style="font-size: 1.1rem" @click.stop="removeItem(item)" @keydown.enter.stop.prevent="removeItem(item)" @focus="setInputFocused(true)" @blur="setInputFocused(false)" tabindex="0">close</button> | ||||
|             </div> | ||||
|             {{ item }} | ||||
|           </div> | ||||
|           <input v-show="!readonly" ref="input" v-model="textInput" :disabled="disabled" class="h-full bg-primary focus:outline-none px-1 w-6" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" @paste="inputPaste" /> | ||||
|           <input v-show="!readonly" v-model="textInput" ref="input" :id="identifier" :disabled="disabled" class="h-full bg-primary focus:outline-none px-1 w-6" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" @paste="inputPaste" /> | ||||
|         </div> | ||||
|       </form> | ||||
| 
 | ||||
| @ -66,7 +66,8 @@ export default { | ||||
|       typingTimeout: null, | ||||
|       isFocused: false, | ||||
|       menu: null, | ||||
|       filteredItems: null | ||||
|       filteredItems: null, | ||||
|       inputFocused: false | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
| @ -100,6 +101,9 @@ export default { | ||||
|       } | ||||
| 
 | ||||
|       return this.filteredItems | ||||
|     }, | ||||
|     identifier() { | ||||
|       return Math.random().toString(36).substring(2) | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
| @ -129,6 +133,9 @@ export default { | ||||
|       }, 100) | ||||
|       this.setInputWidth() | ||||
|     }, | ||||
|     setInputFocused(focused) { | ||||
|       this.inputFocused = focused | ||||
|     }, | ||||
|     setInputWidth() { | ||||
|       setTimeout(() => { | ||||
|         var value = this.$refs.input.value | ||||
|  | ||||
| @ -1,20 +1,20 @@ | ||||
| <template> | ||||
|   <div class="w-full"> | ||||
|     <p class="px-1 text-sm font-semibold" :class="disabled ? 'text-gray-400' : ''">{{ label }}</p> | ||||
|     <label :for="identifier" class="px-1 text-sm font-semibold" :class="disabled ? 'text-gray-400' : ''">{{ label }}</label> | ||||
|     <div ref="wrapper" class="relative"> | ||||
|       <form @submit.prevent="submitForm"> | ||||
|         <div ref="inputWrapper" style="min-height: 36px" class="flex-wrap relative w-full shadow-sm flex items-center border border-gray-600 rounded px-2 py-0.5" :class="wrapperClass" @click.stop.prevent="clickWrapper" @mouseup.stop.prevent @mousedown.prevent> | ||||
|           <div v-for="item in selected" :key="item.id" class="rounded-full px-2 py-0.5 m-0.5 text-xs bg-bg flex flex-nowrap whitespace-nowrap items-center justify-center relative min-w-12"> | ||||
|             <div v-if="!disabled" class="w-full h-full rounded-full absolute top-0 left-0 opacity-0 hover:opacity-100 px-1 bg-bg bg-opacity-75 flex items-center justify-end cursor-pointer"> | ||||
|               <span v-if="showEdit" class="material-symbols text-base text-white hover:text-warning mr-1" @click.stop="editItem(item)">edit</span> | ||||
|               <span class="material-symbols text-white hover:text-error" style="font-size: 1.1rem" @click.stop="removeItem(item.id)">close</span> | ||||
|         <div ref="inputWrapper" role="list" style="min-height: 36px" class="flex-wrap relative w-full shadow-sm flex items-center border border-gray-600 rounded px-2 py-0.5" :class="wrapperClass" @click.stop.prevent="clickWrapper" @mouseup.stop.prevent @mousedown.prevent> | ||||
|           <div v-for="item in selected" :key="item.id" role="listitem" class="rounded-full px-2 py-0.5 m-0.5 text-xs bg-bg flex flex-nowrap whitespace-nowrap items-center justify-center relative min-w-12"> | ||||
|             <div v-if="!disabled" class="w-full h-full rounded-full absolute top-0 left-0 opacity-0 hover:opacity-100 px-1 bg-bg bg-opacity-75 flex items-center justify-end cursor-pointer" :class="{ 'opacity-100': inputFocused }"> | ||||
|               <button v-if="showEdit" type="button" :aria-label="$strings.ButtonEdit" class="material-symbols text-base text-white hover:text-warning focus:text-warning mr-1" @click.stop="editItem(item)" @keydown.enter.stop.prevent="editItem(item)" @focus="setInputFocused(true)" @blur="setInputFocused(false)" tabindex="0">edit</button> | ||||
|               <button type="button" :aria-label="$strings.ButtonRemove" class="material-symbols text-white hover:text-error focus:text-error" style="font-size: 1.1rem" @click.stop="removeItem(item.id)" @keydown.enter.stop="removeItem(item.id)" @focus="setInputFocused(true)" @blur="setInputFocused(false)" tabindex="0">close</button> | ||||
|             </div> | ||||
|             {{ item[textKey] }} | ||||
|           </div> | ||||
|           <div v-if="showEdit && !disabled" class="rounded-full cursor-pointer w-6 h-6 mx-0.5 bg-bg flex items-center justify-center"> | ||||
|             <span class="material-symbols text-white hover:text-success pt-px pr-px" style="font-size: 1.1rem" @click.stop="addItem">add</span> | ||||
|             <button type="button" :aria-label="$strings.ButtonAdd" class="material-symbols text-white hover:text-success focus:text-success pt-px pr-px" style="font-size: 1.1rem" @click.stop="addItem" @keydown.enter.stop="addItem" tabindex="0">add</button> | ||||
|           </div> | ||||
|           <input v-show="!readonly" ref="input" v-model="textInput" :disabled="disabled" class="h-full bg-primary focus:outline-none px-1 w-6" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" @paste="inputPaste" /> | ||||
|           <input v-show="!readonly" v-model="textInput" ref="input" :id="identifier" :disabled="disabled" class="h-full bg-primary focus:outline-none px-1 w-6" @keydown="keydownInput" @focus="inputFocus" @blur="inputBlur" @paste="inputPaste" /> | ||||
|         </div> | ||||
|       </form> | ||||
| 
 | ||||
| @ -65,6 +65,7 @@ export default { | ||||
|       currentSearch: null, | ||||
|       typingTimeout: null, | ||||
|       isFocused: false, | ||||
|       inputFocused: false, | ||||
|       menu: null, | ||||
|       items: [] | ||||
|     } | ||||
| @ -102,6 +103,9 @@ export default { | ||||
|     }, | ||||
|     filterData() { | ||||
|       return this.$store.state.libraries.filterData || {} | ||||
|     }, | ||||
|     identifier() { | ||||
|       return Math.random().toString(36).substring(2) | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
| @ -114,6 +118,9 @@ export default { | ||||
|     getIsSelected(itemValue) { | ||||
|       return !!this.selected.find((i) => i.id === itemValue) | ||||
|     }, | ||||
|     setInputFocused(focused) { | ||||
|       this.inputFocused = focused | ||||
|     }, | ||||
|     search() { | ||||
|       if (!this.textInput) return | ||||
|       this.currentSearch = this.textInput | ||||
|  | ||||
| @ -42,6 +42,7 @@ Vue.prototype.$languageCodeOptions = Object.keys(languageCodeMap).map((code) => | ||||
| 
 | ||||
| // iTunes search API uses ISO 3166 country codes: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
 | ||||
| const podcastSearchRegionMap = { | ||||
|   au: { label: 'Australia' }, | ||||
|   br: { label: 'Brasil' }, | ||||
|   be: { label: 'België / Belgique / Belgien' }, | ||||
|   cz: { label: 'Česko' }, | ||||
| @ -57,6 +58,7 @@ const podcastSearchRegionMap = { | ||||
|   hu: { label: 'Magyarország' }, | ||||
|   nl: { label: 'Nederland' }, | ||||
|   no: { label: 'Norge' }, | ||||
|   nz: { label: 'New Zealand' }, | ||||
|   at: { label: 'Österreich' }, | ||||
|   pl: { label: 'Polska' }, | ||||
|   pt: { label: 'Portugal' }, | ||||
|  | ||||
| @ -53,7 +53,17 @@ class Server { | ||||
|     global.RouterBasePath = ROUTER_BASE_PATH | ||||
|     global.XAccel = process.env.USE_X_ACCEL | ||||
|     global.AllowCors = process.env.ALLOW_CORS === '1' | ||||
|     global.DisableSsrfRequestFilter = process.env.DISABLE_SSRF_REQUEST_FILTER === '1' | ||||
| 
 | ||||
|     if (process.env.DISABLE_SSRF_REQUEST_FILTER === '1') { | ||||
|       Logger.info(`[Server] SSRF Request Filter Disabled`) | ||||
|       global.DisableSsrfRequestFilter = () => true | ||||
|     } else if (process.env.SSRF_REQUEST_FILTER_WHITELIST?.length) { | ||||
|       const whitelistedUrls = process.env.SSRF_REQUEST_FILTER_WHITELIST.split(',').map((url) => url.trim()) | ||||
|       if (whitelistedUrls.length) { | ||||
|         Logger.info(`[Server] SSRF Request Filter Whitelisting: ${whitelistedUrls.join(',')}`) | ||||
|         global.DisableSsrfRequestFilter = (url) => whitelistedUrls.includes(new URL(url).hostname) | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     if (!fs.pathExistsSync(global.ConfigPath)) { | ||||
|       fs.mkdirSync(global.ConfigPath) | ||||
|  | ||||
| @ -277,8 +277,8 @@ module.exports.downloadFile = (url, filepath, contentTypeFilter = null) => { | ||||
|         'User-Agent': 'audiobookshelf (+https://audiobookshelf.org)' | ||||
|       }, | ||||
|       timeout: 30000, | ||||
|       httpAgent: global.DisableSsrfRequestFilter ? null : ssrfFilter(url), | ||||
|       httpsAgent: global.DisableSsrfRequestFilter ? null : ssrfFilter(url) | ||||
|       httpAgent: global.DisableSsrfRequestFilter?.(url) ? null : ssrfFilter(url), | ||||
|       httpsAgent: global.DisableSsrfRequestFilter?.(url) ? null : ssrfFilter(url) | ||||
|     }) | ||||
|       .then((response) => { | ||||
|         // Validate content type
 | ||||
|  | ||||
| @ -244,8 +244,8 @@ module.exports.getPodcastFeed = (feedUrl, excludeEpisodeMetadata = false) => { | ||||
|       Accept: 'application/rss+xml, application/xhtml+xml, application/xml, */*;q=0.8', | ||||
|       'User-Agent': userAgent | ||||
|     }, | ||||
|     httpAgent: global.DisableSsrfRequestFilter ? null : ssrfFilter(feedUrl), | ||||
|     httpsAgent: global.DisableSsrfRequestFilter ? null : ssrfFilter(feedUrl) | ||||
|     httpAgent: global.DisableSsrfRequestFilter?.(feedUrl) ? null : ssrfFilter(feedUrl), | ||||
|     httpsAgent: global.DisableSsrfRequestFilter?.(feedUrl) ? null : ssrfFilter(feedUrl) | ||||
|   }) | ||||
|     .then(async (data) => { | ||||
|       // Adding support for ios-8859-1 encoded RSS feeds.
 | ||||
|  | ||||
| @ -7,7 +7,7 @@ module.exports.zipDirectoryPipe = (path, filename, res) => { | ||||
|     res.attachment(filename) | ||||
| 
 | ||||
|     const archive = archiver('zip', { | ||||
|       zlib: { level: 9 } // Sets the compression level.
 | ||||
|       zlib: { level: 0 } // Sets the compression level.
 | ||||
|     }) | ||||
| 
 | ||||
|     // listen for all archive data to be written
 | ||||
| @ -49,4 +49,4 @@ module.exports.zipDirectoryPipe = (path, filename, res) => { | ||||
| 
 | ||||
|     archive.finalize() | ||||
|   }) | ||||
| } | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user