2021-11-14 19:59:34 -06:00
< template >
2021-12-31 16:57:53 -06:00
< div class = "w-full h-full min-h-full relative" >
2023-09-10 08:38:11 -05:00
< div v-if = "attemptingConnection" class="w-full pt-4 flex items-center justify-center" >
< widgets-loading-spinner / >
< p class = "pl-4" > Attempting server connection ... < / p >
< / div >
2023-06-04 17:14:26 -05:00
< div v-if = "shelves.length && isLoading" class="w-full pt-4 flex items-center justify-center" >
< widgets-loading-spinner / >
< p class = "pl-4" > Loading server data ... < / p >
< / div >
< div class = "w-full" : class = "{ 'py-6': altViewEnabled }" >
2022-04-03 17:07:26 -05:00
< template v-for = "(shelf, index) in shelves" >
< bookshelf-shelf :key = "shelf.id" :label = "shelf.label" :entities = "shelf.entities" :type = "shelf.type" : style = "{ zIndex: shelves.length - index }" / >
< / template >
< / div >
2021-12-04 19:56:29 -06:00
2023-06-04 17:14:26 -05:00
< div v-if = "!shelves.length && !isLoading" class="absolute top-0 left-0 w-full h-full flex items-center justify-center" >
2021-12-04 19:56:29 -06:00
< div >
< p class = "mb-4 text-center text-xl" >
2021-12-31 16:57:53 -06:00
Bookshelf empty
2022-04-03 14:24:17 -05:00
< span v-show = "user" >
2021-12-31 16:57:53 -06:00
for library
< strong > { { currentLibraryName } } < / strong >
< / span >
2021-12-04 19:56:29 -06:00
< / p >
2022-04-03 14:24:17 -05:00
< div class = "w-full" v-if = "!user" >
2021-12-04 19:56:29 -06:00
< div class = "flex justify-center items-center mb-3" >
< span class = "material-icons text-error text-lg" > cloud _off < / span >
< p class = "pl-2 text-error text-sm" > Audiobookshelf server not connected . < / p >
< / div >
< / div >
< div class = "flex justify-center" >
2022-04-03 14:24:17 -05:00
< ui-btn v-if = "!user" small @click="$router.push('/connect')" class="w-32" > Connect < / ui -btn >
2021-12-04 19:56:29 -06:00
< / div >
< / div >
< / div >
2023-06-04 17:14:26 -05:00
< div v -else -if = " ! shelves.length & & isLoading & & ! attemptingConnection " class = "absolute top-0 left-0 z-50 w-full h-full flex items-center justify-center" >
< ui-loading-indicator text = "Loading..." / >
< / div >
2021-11-14 19:59:34 -06:00
< / div >
< / template >
< script >
export default {
2023-06-04 17:14:26 -05:00
props : { } ,
2021-11-14 19:59:34 -06:00
data ( ) {
return {
2021-12-04 19:56:29 -06:00
shelves : [ ] ,
2023-01-30 15:38:09 -06:00
isFirstNetworkConnection : true ,
2023-01-28 17:31:52 -06:00
lastServerFetch : 0 ,
2023-01-30 15:38:09 -06:00
lastServerFetchLibraryId : null ,
lastLocalFetch : 0 ,
2023-06-04 17:14:26 -05:00
localLibraryItems : [ ] ,
isLoading : false
2021-11-14 19:59:34 -06:00
}
} ,
2022-12-10 14:17:18 -06:00
watch : {
networkConnected ( newVal ) {
// Update shelves when network connect status changes
2023-01-30 15:38:09 -06:00
console . log ( ` [categories] Network changed to ${ newVal } - fetch categories. ${ this . lastServerFetch } / ${ this . lastLocalFetch } ` )
2023-01-28 14:20:00 -06:00
if ( newVal ) {
2023-01-30 15:38:09 -06:00
// Fetch right away the first time network connects
if ( this . isFirstNetworkConnection ) {
this . isFirstNetworkConnection = false
console . log ( ` [categories] networkConnected true first network connection. lastServerFetch= ${ this . lastServerFetch } ` )
this . fetchCategories ( )
return
}
2023-01-29 16:04:03 -06:00
setTimeout ( ( ) => {
// Using timeout because making this fetch as soon as network gets connected will often fail on Android
2023-01-30 15:38:09 -06:00
console . log ( ` [categories] networkConnected true so fetching categories. lastServerFetch= ${ this . lastServerFetch } ` )
this . fetchCategories ( )
2023-01-29 16:04:03 -06:00
} , 4000 )
2023-01-28 14:20:00 -06:00
} else {
2023-01-30 15:38:09 -06:00
console . log ( ` [categories] networkConnected false so fetching categories ` )
2023-01-28 14:20:00 -06:00
this . fetchCategories ( )
}
2022-12-10 14:17:18 -06:00
}
} ,
2021-11-14 19:59:34 -06:00
computed : {
2022-04-03 14:24:17 -05:00
user ( ) {
return this . $store . state . user . user
} ,
2022-12-10 14:17:18 -06:00
networkConnected ( ) {
return this . $store . state . networkConnected
2021-12-04 19:56:29 -06:00
} ,
2023-02-17 17:14:49 -06:00
isIos ( ) {
return this . $platform === 'ios'
} ,
2021-12-04 19:56:29 -06:00
currentLibraryName ( ) {
return this . $store . getters [ 'libraries/getCurrentLibraryName' ]
2021-11-14 19:59:34 -06:00
} ,
2021-12-04 19:56:29 -06:00
currentLibraryId ( ) {
return this . $store . state . libraries . currentLibraryId
2022-07-27 18:21:10 -05:00
} ,
2022-12-10 14:17:18 -06:00
currentLibraryMediaType ( ) {
return this . $store . getters [ 'libraries/getCurrentLibraryMediaType' ]
} ,
2023-01-29 16:04:03 -06:00
currentLibraryIsPodcast ( ) {
return this . currentLibraryMediaType === 'podcast'
} ,
2022-07-27 18:21:10 -05:00
altViewEnabled ( ) {
return this . $store . getters [ 'getAltViewEnabled' ]
2022-08-07 11:00:35 -05:00
} ,
localMediaProgress ( ) {
return this . $store . state . globals . localMediaProgress
2023-02-17 17:47:46 -06:00
} ,
2023-06-04 17:14:26 -05:00
attemptingConnection ( ) {
return this . $store . state . attemptingConnection
2021-11-14 19:59:34 -06:00
}
} ,
methods : {
2023-06-04 17:14:26 -05:00
getLocalMediaItemCategories ( ) {
const localMedia = this . localLibraryItems
if ( ! localMedia ? . length ) return [ ]
2022-04-07 18:46:58 -05:00
2023-01-29 16:04:03 -06:00
const categories = [ ]
const books = [ ]
const podcasts = [ ]
const booksContinueListening = [ ]
const podcastEpisodesContinueListening = [ ]
2022-04-03 17:07:26 -05:00
localMedia . forEach ( ( item ) => {
if ( item . mediaType == 'book' ) {
2023-01-29 16:04:03 -06:00
item . progress = this . $store . getters [ 'globals/getLocalMediaProgressById' ] ( item . id )
if ( item . progress && ! item . progress . isFinished && item . progress . progress > 0 ) booksContinueListening . push ( item )
2022-04-03 17:07:26 -05:00
books . push ( item )
} else if ( item . mediaType == 'podcast' ) {
2023-01-29 16:04:03 -06:00
const podcastEpisodeItemCloner = { ... item }
item . media . episodes = item . media . episodes . map ( ( ep ) => {
ep . progress = this . $store . getters [ 'globals/getLocalMediaProgressById' ] ( item . id , ep . id )
if ( ep . progress && ! ep . progress . isFinished && ep . progress . progress > 0 ) {
podcastEpisodesContinueListening . push ( {
... podcastEpisodeItemCloner ,
recentEpisode : ep
} )
}
return ep
} )
2022-04-03 17:07:26 -05:00
podcasts . push ( item )
}
} )
2023-01-29 16:04:03 -06:00
// Local continue listening shelves, only shown offline
if ( booksContinueListening . length ) {
categories . push ( {
id : 'local-books-continue' ,
label : 'Continue Books' ,
type : 'book' ,
localOnly : true ,
entities : booksContinueListening . sort ( ( a , b ) => {
if ( a . progress && b . progress ) {
return b . progress . lastUpdate > a . progress . lastUpdate ? 1 : - 1
}
return 0
} )
} )
}
if ( podcastEpisodesContinueListening . length ) {
categories . push ( {
id : 'local-episodes-continue' ,
label : 'Continue Episodes' ,
type : 'episode' ,
localOnly : true ,
entities : podcastEpisodesContinueListening . sort ( ( a , b ) => {
if ( a . recentEpisode . progress && b . recentEpisode . progress ) {
return b . recentEpisode . progress . lastUpdate > a . recentEpisode . progress . lastUpdate ? 1 : - 1
}
return 0
} )
} )
}
// Local books and local podcast shelves
2022-04-03 17:07:26 -05:00
if ( books . length ) {
categories . push ( {
id : 'local-books' ,
label : 'Local Books' ,
type : 'book' ,
2022-08-07 11:00:35 -05:00
entities : books . sort ( ( a , b ) => {
if ( a . progress && a . progress . isFinished ) return 1
else if ( b . progress && b . progress . isFinished ) return - 1
2023-01-29 16:04:03 -06:00
else if ( a . progress && b . progress ) {
return b . progress . lastUpdate > a . progress . lastUpdate ? 1 : - 1
}
2022-08-07 11:00:35 -05:00
return 0
} )
2021-12-04 19:56:29 -06:00
} )
2022-04-03 17:07:26 -05:00
}
if ( podcasts . length ) {
categories . push ( {
id : 'local-podcasts' ,
label : 'Local Podcasts' ,
type : 'podcast' ,
2022-08-07 08:38:30 -05:00
entities : podcasts
2021-12-04 19:56:29 -06:00
} )
2022-04-03 17:07:26 -05:00
}
2022-04-07 18:46:58 -05:00
2022-04-03 17:07:26 -05:00
return categories
} ,
2022-04-07 19:59:23 -05:00
async fetchCategories ( ) {
2023-01-30 15:38:09 -06:00
console . log ( ` [categories] fetchCategories networkConnected= ${ this . networkConnected } , lastServerFetch= ${ this . lastServerFetch } , lastLocalFetch= ${ this . lastLocalFetch } ` )
// TODO: Find a better way to keep the shelf up-to-date with local vs server library because this is a disaster
2023-06-04 17:14:26 -05:00
const isConnectedToServerWithInternet = this . user && this . currentLibraryId && this . networkConnected
if ( isConnectedToServerWithInternet ) {
2023-01-30 15:38:09 -06:00
if ( this . lastServerFetch && Date . now ( ) - this . lastServerFetch < 5000 && this . lastServerFetchLibraryId == this . currentLibraryId ) {
console . log ( ` [categories] fetchCategories server fetch was ${ Date . now ( ) - this . lastServerFetch } ms ago so not doing it. ` )
return
} else {
console . log ( ` [categories] fetchCategories fetching from server. Last was ${ this . lastServerFetch ? Date . now ( ) - this . lastServerFetch + 'ms' : 'Never' } ago. lastServerFetchLibraryId= ${ this . lastServerFetchLibraryId } and currentLibraryId= ${ this . currentLibraryId } ` )
this . lastServerFetchLibraryId = this . currentLibraryId
this . lastServerFetch = Date . now ( )
this . lastLocalFetch = 0
}
} else {
if ( this . lastLocalFetch && Date . now ( ) - this . lastLocalFetch < 5000 ) {
console . log ( ` [categories] fetchCategories local fetch was ${ Date . now ( ) - this . lastLocalFetch } ms ago so not doing it. ` )
return
} else {
console . log ( ` [categories] fetchCategories fetching from local. Last was ${ this . lastLocalFetch ? Date . now ( ) - this . lastLocalFetch + 'ms' : 'Never' } ago ` )
this . lastServerFetchLibraryId = null
this . lastServerFetch = 0
this . lastLocalFetch = Date . now ( )
}
2022-04-04 19:08:27 -05:00
}
2023-01-30 15:38:09 -06:00
2023-02-17 17:47:46 -06:00
this . isLoading = true
2022-04-03 17:07:26 -05:00
2023-06-04 17:14:26 -05:00
// Set local library items first
this . localLibraryItems = await this . $db . getLocalLibraryItems ( )
const localCategories = this . getLocalMediaItemCategories ( )
this . shelves = localCategories
console . log ( '[categories] Local shelves set' , this . shelves . length , this . lastLocalFetch )
if ( isConnectedToServerWithInternet ) {
2023-06-24 14:45:25 -05:00
const categories = await this . $axios . $get ( ` /api/libraries/ ${ this . currentLibraryId } /personalized?minified=1&include=rssfeed ` ) . catch ( ( error ) => {
2023-01-30 15:38:09 -06:00
console . error ( '[categories] Failed to fetch categories' , error )
2022-04-07 18:46:58 -05:00
return [ ]
} )
2023-01-30 15:38:09 -06:00
if ( ! categories . length ) {
// Failed to load categories so use local shelves
console . warn ( ` [categories] Failed to get server categories so using local categories ` )
this . lastServerFetch = 0
this . lastLocalFetch = Date . now ( )
2023-02-17 17:47:46 -06:00
this . isLoading = false
2023-01-30 15:38:09 -06:00
console . log ( '[categories] Local shelves set from failure' , this . shelves . length , this . lastLocalFetch )
return
}
2022-12-10 14:17:18 -06:00
this . shelves = categories . map ( ( cat ) => {
2022-08-21 18:03:27 -05:00
if ( cat . type == 'book' || cat . type == 'podcast' || cat . type == 'episode' ) {
2022-04-07 18:46:58 -05:00
// Map localLibraryItem to entities
cat . entities = cat . entities . map ( ( entity ) => {
2022-12-10 14:17:18 -06:00
const localLibraryItem = this . localLibraryItems . find ( ( lli ) => {
2022-04-07 18:46:58 -05:00
return lli . libraryItemId == entity . id
} )
if ( localLibraryItem ) {
entity . localLibraryItem = localLibraryItem
}
return entity
} )
}
return cat
} )
2022-12-10 14:17:18 -06:00
// Only add the local shelf with the same media type
2023-01-29 16:04:03 -06:00
const localShelves = localCategories . filter ( ( cat ) => cat . type === this . currentLibraryMediaType && ! cat . localOnly )
2022-12-10 14:17:18 -06:00
this . shelves . push ( ... localShelves )
2023-01-30 15:38:09 -06:00
console . log ( '[categories] Server shelves set' , this . shelves . length , this . lastServerFetch )
2022-04-03 17:07:26 -05:00
}
2022-12-10 14:17:18 -06:00
2023-02-17 17:47:46 -06:00
this . isLoading = false
2021-12-04 19:56:29 -06:00
} ,
2023-01-30 15:38:09 -06:00
libraryChanged ( ) {
2022-04-07 18:46:58 -05:00
if ( this . currentLibraryId ) {
2023-01-30 15:38:09 -06:00
console . log ( ` [categories] libraryChanged so fetching categories ` )
this . fetchCategories ( )
2021-12-05 18:31:47 -06:00
}
} ,
audiobookAdded ( audiobook ) {
// TODO: Check if audiobook would be on this shelf
if ( ! this . search ) {
this . fetchCategories ( )
}
} ,
audiobookUpdated ( audiobook ) {
this . shelves . forEach ( ( shelf ) => {
if ( shelf . type === 'books' ) {
shelf . entities = shelf . entities . map ( ( ent ) => {
if ( ent . id === audiobook . id ) {
return audiobook
}
return ent
} )
} else if ( shelf . type === 'series' ) {
shelf . entities . forEach ( ( ent ) => {
ent . books = ent . books . map ( ( book ) => {
if ( book . id === audiobook . id ) return audiobook
return book
} )
} )
}
} )
} ,
removeBookFromShelf ( audiobook ) {
this . shelves . forEach ( ( shelf ) => {
if ( shelf . type === 'books' ) {
shelf . entities = shelf . entities . filter ( ( ent ) => {
return ent . id !== audiobook . id
} )
} else if ( shelf . type === 'series' ) {
shelf . entities . forEach ( ( ent ) => {
ent . books = ent . books . filter ( ( book ) => {
return book . id !== audiobook . id
} )
} )
}
} )
} ,
initListeners ( ) {
this . $eventBus . $on ( 'library-changed' , this . libraryChanged )
} ,
removeListeners ( ) {
this . $eventBus . $off ( 'library-changed' , this . libraryChanged )
2021-12-04 19:56:29 -06:00
}
} ,
mounted ( ) {
2021-12-05 18:31:47 -06:00
this . initListeners ( )
2023-01-30 15:38:09 -06:00
console . log ( ` [categories] mounted so fetching categories ` )
2022-04-07 19:59:23 -05:00
this . fetchCategories ( )
2021-11-14 19:59:34 -06:00
} ,
2021-12-04 19:56:29 -06:00
beforeDestroy ( ) {
2021-12-05 18:31:47 -06:00
this . removeListeners ( )
2021-12-04 19:56:29 -06:00
}
2021-11-14 19:59:34 -06:00
}
< / script >