2021-08-17 17:01:11 -05:00
< template >
< div class = "w-full h-16 bg-primary relative" >
2022-12-17 16:36:41 -06:00
< div id = "appbar" class = "absolute top-0 bottom-0 left-0 w-full h-full px-2 md:px-6 py-1 z-60" >
2021-08-17 17:01:11 -05:00
< div class = "flex h-full items-center" >
2022-05-09 15:12:55 -05:00
< nuxt-link to = "/" >
2023-02-11 15:02:56 -06:00
< img src = "~static/icon.svg" :alt = "$strings.ButtonHome" class = "w-8 min-w-8 h-8 mr-2 sm:w-10 sm:min-w-10 sm:h-10 sm:mr-4" / >
2022-05-09 15:12:55 -05:00
< / nuxt-link >
< nuxt-link to = "/" >
2023-02-11 15:02:56 -06:00
< h1 class = "text-xl mr-6 hidden lg:block hover:underline" > audiobookshelf < span v-if = "showExperimentalFeatures" class="material-icons text-lg text-warning pr-1" > logo_dev < / span > < / h1 >
2022-05-09 15:12:55 -05:00
< / nuxt-link >
2021-10-04 22:11:42 -05:00
2022-07-26 18:54:32 -05:00
< ui-libraries-dropdown class = "mr-2" / >
2021-10-04 22:11:42 -05:00
2022-08-20 14:32:38 -05:00
< controls-global-search v-if = "currentLibrary" class="mr-1 sm:mr-0" / >
2021-08-17 17:01:11 -05:00
< div class = "flex-grow" / >
2022-10-02 14:16:17 -05:00
< widgets-notification-widget class = "hidden md:block" / >
2021-10-02 15:36:33 -05:00
2022-02-23 17:01:11 -06:00
< ui-tooltip v-if = "isChromecastInitialized && !isHttps" direction="bottom" text="Casting requires a secure connection" class="flex items-center" >
2022-11-21 07:18:10 -06:00
< span class = "material-icons-outlined text-2xl text-warning text-opacity-50" > cast < / span >
2022-02-23 17:01:11 -06:00
< / ui-tooltip >
2022-06-19 15:43:45 -05:00
< div v-if = "isChromecastInitialized" class="w-6 min-w-6 h-6 ml-2 mr-1 sm:mx-2 cursor-pointer" >
2022-02-22 17:33:55 -06:00
< google-cast-launcher > < / google-cast-launcher >
< / div >
2022-12-28 22:59:27 +01:00
< nuxt-link v-if = "currentLibrary" to="/config/stats" class="hover:text-gray-200 cursor-pointer w-8 h-8 hidden sm:flex items-center justify-center mx-1" >
2022-12-05 17:12:53 -06:00
< ui-tooltip :text = "$strings.HeaderYourStats" direction = "bottom" class = "flex items-center" >
2022-12-05 23:16:27 +01:00
< span class = "material-icons text-2xl" aria -label = " User Stats " role = "button" > equalizer < / span >
< / ui-tooltip >
2021-11-07 19:52:13 -06:00
< / nuxt-link >
2022-12-28 22:59:27 +01:00
< nuxt-link v-if = "userCanUpload && currentLibrary" to="/upload" class="hover:text-gray-200 cursor-pointer w-8 h-8 flex items-center justify-center mx-1" >
2022-12-05 17:12:58 -06:00
< ui-tooltip :text = "$strings.ButtonUpload" direction = "bottom" class = "flex items-center" >
2022-12-05 23:16:27 +01:00
< span class = "material-icons text-2xl" aria -label = " Upload Media " role = "button" > upload < / span >
< / ui-tooltip >
2021-09-13 20:18:58 -05:00
< / nuxt-link >
2022-12-28 22:59:27 +01:00
< nuxt-link v-if = "userIsAdminOrUp" to="/config" class="hover:text-gray-200 cursor-pointer w-8 h-8 flex items-center justify-center mx-1" >
2022-12-05 17:13:03 -06:00
< ui-tooltip :text = "$strings.HeaderSettings" direction = "bottom" class = "flex items-center" >
2022-12-05 23:16:27 +01:00
< span class = "material-icons text-2xl" aria -label = " System Settings " role = "button" > settings < / span >
< / ui-tooltip >
2021-08-17 17:01:11 -05:00
< / nuxt-link >
2022-12-28 22:59:27 +01:00
< nuxt-link to = "/account" class = "relative w-9 h-9 md:w-32 bg-fg border border-gray-500 rounded shadow-sm ml-1.5 sm:ml-3 md:ml-5 md:pl-3 md:pr-10 py-2 text-left sm:text-sm cursor-pointer hover:bg-bg hover:bg-opacity-40" aria -haspopup = " listbox " aria -expanded = " true " >
2021-11-04 17:35:59 -05:00
< span class = "items-center hidden md:flex" >
2021-09-05 13:21:02 -05:00
< span class = "block truncate" > { { username } } < / span >
< / span >
2021-11-04 17:35:59 -05:00
< span class = "h-full md:ml-3 md:absolute inset-y-0 md:right-0 flex items-center justify-center md:pr-2 pointer-events-none" >
2022-11-19 12:09:05 -06:00
< span class = "material-icons text-xl text-gray-100" > person < / span >
2021-09-05 13:21:02 -05:00
< / span >
< / nuxt-link >
2021-08-17 17:01:11 -05:00
< / div >
2022-11-30 17:09:00 -06:00
< div v-show = "numMediaItemsSelected" class="absolute top-0 left-0 w-full h-full px-4 bg-primary flex items-center" >
< h1 class = "text-lg md:text-2xl px-4" > { { $getString ( 'MessageItemsSelected' , [ numMediaItemsSelected ] ) } } < / h1 >
2021-08-26 18:32:05 -05:00
< div class = "flex-grow" / >
2022-11-30 17:09:00 -06:00
< ui-btn v-if = "!isPodcastLibrary && selectedMediaItemsArePlayable" color="success" :padding-x="4" small class="flex items-center h-9 mr-2" @click="playSelectedItems" >
2022-11-21 07:18:10 -06:00
< span class = "material-icons text-2xl -ml-2 pr-1 text-white" > play _arrow < / span >
2022-11-19 10:20:10 -06:00
{ { $strings . ButtonPlay } }
< / ui-btn >
2023-01-03 18:00:01 -06:00
< ui-tooltip v-if = "isBookLibrary" :text="selectedIsFinished ? $strings.MessageMarkAsNotFinished : $strings.MessageMarkAsFinished" direction="bottom" >
2022-03-17 13:33:22 -05:00
< ui-read-icon-btn :disabled = "processingBatch" :is-read = "selectedIsFinished" @click ="toggleBatchRead" class = "mx-1.5" / >
2021-09-16 08:37:09 -05:00
< / ui-tooltip >
2023-01-03 18:00:01 -06:00
< ui-tooltip v-if = "userCanUpdate && isBookLibrary" :text="$strings.LabelAddToCollection" direction="bottom" >
2021-11-27 16:01:53 -06:00
< ui-icon-btn :disabled = "processingBatch" icon = "collections_bookmark" @click ="batchAddToCollectionClick" class = "mx-1.5" / >
< / ui-tooltip >
2022-11-19 11:27:08 -06:00
< template v-if = "userCanUpdate" >
2022-12-15 17:57:42 -06:00
< ui-tooltip :text = "$strings.LabelEdit" direction = "bottom" >
2022-11-19 10:20:10 -06:00
< ui-icon-btn :disabled = "processingBatch" icon = "edit" bg -color = " warning " class = "mx-1.5" @click ="batchEditClick" / >
2022-04-28 15:44:07 -07:00
< / ui-tooltip >
2021-09-06 17:42:15 -05:00
< / template >
2022-11-08 17:10:08 -06:00
< ui-tooltip v-if = "userCanDelete" :text="$strings.ButtonRemove" direction="bottom" >
2022-11-19 10:20:10 -06:00
< ui-icon-btn :disabled = "processingBatch" icon = "delete" bg -color = " error " class = "mx-1.5" @click ="batchDeleteClick" / >
2022-04-28 15:44:07 -07:00
< / ui-tooltip >
2023-04-02 16:13:18 -05:00
< ui-context-menu-dropdown v-if = "contextMenuItems.length && !processingBatch" :items="contextMenuItems" class="ml-1" @action="contextMenuAction" / >
< ui-tooltip :text = "$strings.LabelDeselectAll" direction = "bottom" class = "flex items-center" >
< span class = "material-icons text-3xl px-4 hover:text-gray-100 cursor-pointer" : class = "processingBatch ? 'text-gray-400' : ''" @click ="cancelSelectionMode" > close < / span >
2022-04-28 15:44:07 -07:00
< / ui-tooltip >
2021-08-26 18:32:05 -05:00
< / div >
2021-08-17 17:01:11 -05:00
< / div >
< / div >
< / template >
< script >
export default {
data ( ) {
return {
2022-11-19 10:20:10 -06:00
totalEntities : 0
2021-08-17 17:01:11 -05:00
}
} ,
computed : {
2021-10-04 22:11:42 -05:00
currentLibrary ( ) {
return this . $store . getters [ 'libraries/getCurrentLibrary' ]
} ,
libraryName ( ) {
return this . currentLibrary ? this . currentLibrary . name : 'unknown'
} ,
2022-03-26 15:23:25 -05:00
libraryMediaType ( ) {
return this . currentLibrary ? this . currentLibrary . mediaType : null
} ,
isPodcastLibrary ( ) {
return this . libraryMediaType === 'podcast'
} ,
2023-01-03 18:00:01 -06:00
isBookLibrary ( ) {
return this . libraryMediaType === 'book'
} ,
2021-09-28 06:44:40 -05:00
isHome ( ) {
2021-10-04 22:11:42 -05:00
return this . $route . name === 'library-library'
2021-09-28 06:44:40 -05:00
} ,
2021-08-17 17:01:11 -05:00
user ( ) {
2021-08-23 18:31:04 -05:00
return this . $store . state . user . user
2021-08-17 17:01:11 -05:00
} ,
2022-05-03 19:16:16 -05:00
userIsAdminOrUp ( ) {
return this . $store . getters [ 'user/getIsAdminOrUp' ]
2021-08-27 07:01:47 -05:00
} ,
2021-08-17 17:01:11 -05:00
username ( ) {
return this . user ? this . user . username : 'err'
2021-08-26 18:32:05 -05:00
} ,
2022-11-30 17:09:00 -06:00
numMediaItemsSelected ( ) {
return this . selectedMediaItems . length
2021-08-26 18:32:05 -05:00
} ,
2022-11-30 17:09:00 -06:00
selectedMediaItems ( ) {
return this . $store . state . globals . selectedMediaItems
} ,
selectedMediaItemsArePlayable ( ) {
2022-12-12 17:36:53 -06:00
return ! this . selectedMediaItems . some ( ( i ) => ! i . hasTracks )
2021-08-26 18:32:05 -05:00
} ,
2022-03-26 11:59:34 -05:00
userMediaProgress ( ) {
return this . $store . state . user . user . mediaProgress || [ ]
2021-09-16 08:37:09 -05:00
} ,
2021-09-06 17:42:15 -05:00
userCanUpdate ( ) {
return this . $store . getters [ 'user/getUserCanUpdate' ]
} ,
userCanDelete ( ) {
return this . $store . getters [ 'user/getUserCanDelete' ]
2021-09-16 08:37:09 -05:00
} ,
2021-09-18 12:45:34 -05:00
userCanUpload ( ) {
return this . $store . getters [ 'user/getUserCanUpload' ]
} ,
2022-03-17 13:33:22 -05:00
selectedIsFinished ( ) {
// Find an item that is not finished, if none then all items finished
2022-11-30 17:09:00 -06:00
return ! this . selectedMediaItems . find ( ( item ) => {
const itemProgress = this . userMediaProgress . find ( ( lip ) => lip . libraryItemId === item . id )
2022-03-17 13:33:22 -05:00
return ! itemProgress || ! itemProgress . isFinished
2021-09-16 08:37:09 -05:00
} )
2021-09-17 14:15:15 -05:00
} ,
processingBatch ( ) {
return this . $store . state . processingBatch
2021-10-02 15:36:33 -05:00
} ,
showExperimentalFeatures ( ) {
return this . $store . state . showExperimentalFeatures
2022-02-22 17:33:55 -06:00
} ,
isChromecastEnabled ( ) {
return this . $store . getters [ 'getServerSetting' ] ( 'chromecastEnabled' )
} ,
isChromecastInitialized ( ) {
return this . $store . state . globals . isChromecastInitialized
2022-02-23 17:01:11 -06:00
} ,
isHttps ( ) {
return location . protocol === 'https:' || process . env . NODE _ENV === 'development'
2023-04-02 16:13:18 -05:00
} ,
contextMenuItems ( ) {
if ( ! this . userIsAdminOrUp ) return [ ]
const options = [
{
text : this . $strings . ButtonQuickMatch ,
action : 'quick-match'
}
]
if ( ! this . isPodcastLibrary && this . selectedMediaItemsArePlayable ) {
options . push ( {
text : 'Quick Embed Metadata' ,
action : 'quick-embed'
} )
}
return options
2021-08-17 17:01:11 -05:00
}
} ,
methods : {
2023-04-02 16:13:18 -05:00
requestBatchQuickEmbed ( ) {
const payload = {
message : 'Warning! Quick embed will not backup your audio files. Make sure that you have a backup of your audio files. <br><br>Would you like to continue?' ,
callback : ( confirmed ) => {
if ( confirmed ) {
this . $axios
. $post ( ` /api/tools/batch/embed-metadata ` , {
libraryItemIds : this . selectedMediaItems . map ( ( i ) => i . id )
} )
. then ( ( ) => {
console . log ( 'Audio metadata embed started' )
this . cancelSelectionMode ( )
} )
. catch ( ( error ) => {
console . error ( 'Audio metadata embed failed' , error )
const errorMsg = error . response . data || 'Failed to embed metadata'
this . $toast . error ( errorMsg )
} )
}
} ,
type : 'yesNo'
}
this . $store . commit ( 'globals/setConfirmPrompt' , payload )
} ,
contextMenuAction ( action ) {
if ( action === 'quick-embed' ) {
this . requestBatchQuickEmbed ( )
} else if ( action === 'quick-match' ) {
this . batchAutoMatchClick ( )
}
} ,
2022-11-19 10:20:10 -06:00
async playSelectedItems ( ) {
this . $store . commit ( 'setProcessingBatch' , true )
2022-11-30 17:09:00 -06:00
const libraryItemIds = this . selectedMediaItems . map ( ( i ) => i . id )
2022-12-12 17:36:53 -06:00
const libraryItems = await this . $axios
. $post ( ` /api/items/batch/get ` , { libraryItemIds } )
. then ( ( res ) => res . libraryItems )
. catch ( ( error ) => {
const errorMsg = error . response . data || 'Failed to get items'
console . error ( errorMsg , error )
this . $toast . error ( errorMsg )
return [ ]
} )
2022-11-19 10:20:10 -06:00
if ( ! libraryItems . length ) {
this . $store . commit ( 'setProcessingBatch' , false )
return
}
const queueItems = [ ]
libraryItems . forEach ( ( item ) => {
2023-01-03 18:00:01 -06:00
let subtitle = ''
if ( item . mediaType === 'book' ) subtitle = item . media . metadata . authors . map ( ( au ) => au . name ) . join ( ', ' )
else if ( item . mediaType === 'music' ) subtitle = item . media . metadata . artists . join ( ', ' )
2022-11-19 10:20:10 -06:00
queueItems . push ( {
libraryItemId : item . id ,
libraryId : item . libraryId ,
episodeId : null ,
title : item . media . metadata . title ,
2023-01-03 18:00:01 -06:00
subtitle ,
2022-11-19 10:20:10 -06:00
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 )
2022-11-30 17:09:00 -06:00
this . $store . commit ( 'globals/resetSelectedMediaItems' , [ ] )
2022-11-19 10:20:10 -06:00
this . $eventBus . $emit ( 'bookshelf_clear_selection' )
} ,
2021-08-26 18:32:05 -05:00
cancelSelectionMode ( ) {
2022-11-19 10:20:10 -06:00
if ( this . processingBatch ) return
2022-11-30 17:09:00 -06:00
this . $store . commit ( 'globals/resetSelectedMediaItems' , [ ] )
2022-10-28 18:10:19 -05:00
this . $eventBus . $emit ( 'bookshelf_clear_selection' )
2021-08-26 18:32:05 -05:00
} ,
2021-09-16 08:37:09 -05:00
toggleBatchRead ( ) {
2021-09-17 14:15:15 -05:00
this . $store . commit ( 'setProcessingBatch' , true )
2022-11-30 17:09:00 -06:00
const newIsFinished = ! this . selectedIsFinished
const updateProgressPayloads = this . selectedMediaItems . map ( ( item ) => {
2021-09-16 08:37:09 -05:00
return {
2022-11-30 17:09:00 -06:00
libraryItemId : item . id ,
2022-03-17 13:33:22 -05:00
isFinished : newIsFinished
2021-09-16 08:37:09 -05:00
}
} )
2022-04-23 17:17:05 -05:00
console . log ( 'Progress payloads' , updateProgressPayloads )
2021-09-16 08:37:09 -05:00
this . $axios
2022-03-17 13:33:22 -05:00
. patch ( ` /api/me/progress/batch/update ` , updateProgressPayloads )
2021-09-16 08:37:09 -05:00
. then ( ( ) => {
this . $toast . success ( 'Batch update success!' )
this . $store . commit ( 'setProcessingBatch' , false )
2022-11-30 17:09:00 -06:00
this . $store . commit ( 'globals/resetSelectedMediaItems' , [ ] )
2022-10-28 18:10:19 -05:00
this . $eventBus . $emit ( 'bookshelf_clear_selection' )
2021-09-16 08:37:09 -05:00
} )
. catch ( ( error ) => {
this . $toast . error ( 'Batch update failed' )
console . error ( 'Failed to batch update read/not read' , error )
this . $store . commit ( 'setProcessingBatch' , false )
} )
} ,
2021-08-26 18:32:05 -05:00
batchDeleteClick ( ) {
2022-11-30 17:09:00 -06:00
const audiobookText = this . numMediaItemsSelected > 1 ? ` these ${ this . numMediaItemsSelected } items ` : 'this item'
const confirmMsg = ` Are you sure you want to remove ${ audiobookText } ? \ n \ n*Does not delete your files, only removes the items from Audiobookshelf `
2021-09-16 08:37:09 -05:00
if ( confirm ( confirmMsg ) ) {
2021-08-26 18:32:05 -05:00
this . $store . commit ( 'setProcessingBatch' , true )
this . $axios
2022-03-13 17:10:48 -05:00
. $post ( ` /api/items/batch/delete ` , {
2022-11-30 17:09:00 -06:00
libraryItemIds : this . selectedMediaItems . map ( ( i ) => i . id )
2021-08-26 18:32:05 -05:00
} )
. then ( ( ) => {
this . $toast . success ( 'Batch delete success!' )
this . $store . commit ( 'setProcessingBatch' , false )
2022-11-30 17:09:00 -06:00
this . $store . commit ( 'globals/resetSelectedMediaItems' , [ ] )
2022-10-28 18:10:19 -05:00
this . $eventBus . $emit ( 'bookshelf_clear_selection' )
2021-08-26 18:32:05 -05:00
} )
. catch ( ( error ) => {
this . $toast . error ( 'Batch delete failed' )
console . error ( 'Failed to batch delete' , error )
this . $store . commit ( 'setProcessingBatch' , false )
} )
}
} ,
batchEditClick ( ) {
this . $router . push ( '/batch' )
2021-11-27 16:01:53 -06:00
} ,
batchAddToCollectionClick ( ) {
2022-11-11 17:13:10 -06:00
this . $store . commit ( 'globals/setShowBatchCollectionsModal' , true )
2021-11-30 20:02:40 -06:00
} ,
2021-12-01 19:07:03 -06:00
setBookshelfTotalEntities ( totalEntities ) {
this . totalEntities = totalEntities
2022-09-19 16:29:24 +01:00
} ,
batchAutoMatchClick ( ) {
this . $store . commit ( 'globals/setShowBatchQuickMatchModal' , true )
2022-10-01 16:07:30 -05:00
}
2021-08-17 17:01:11 -05:00
} ,
2021-11-30 20:02:40 -06:00
mounted ( ) {
2021-12-01 19:07:03 -06:00
this . $eventBus . $on ( 'bookshelf-total-entities' , this . setBookshelfTotalEntities )
2021-11-30 20:02:40 -06:00
} ,
beforeDestroy ( ) {
2021-12-01 19:07:03 -06:00
this . $eventBus . $off ( 'bookshelf-total-entities' , this . setBookshelfTotalEntities )
2021-11-30 20:02:40 -06:00
}
2021-08-17 17:01:11 -05:00
}
< / script >
< style >
# appbar {
2021-08-18 06:50:24 -05:00
box - shadow : 0 px 5 px 5 px # 11111155 ;
2021-08-17 17:01:11 -05:00
}
2022-04-28 15:44:07 -07:00
< / style >