2022-03-23 17:59:14 -05:00
< template >
< div class = "w-full h-full px-3 py-4 overflow-y-auto" >
< div class = "flex" >
< div class = "w-32" >
< div class = "relative" >
2022-04-05 19:44:14 -05:00
< covers-book-cover :library-item = "libraryItem" :width = "128" :book-cover-aspect-ratio = "bookCoverAspectRatio" / >
2022-04-17 17:54:13 -05:00
< div v-if = "!isPodcast" class="absolute bottom-0 left-0 h-1.5 shadow-sm z-10" :class="userIsFinished ? 'bg-success' : 'bg-yellow-400'" :style="{ width: 128 * progressPercent + 'px' }" > < / div >
2022-03-23 17:59:14 -05:00
< / div >
< / div >
< div class = "flex-grow px-3" >
< h1 class = "text-lg" > { { title } } < / h1 >
<!-- < h3 v-if = "series" class="font-book text-gray-300 text-lg leading-7" > {{ seriesText }} < / h3 > - - >
< p class = "text-sm text-gray-400" > by { { author } } < / p >
< p v-if = "numTracks" class="text-gray-300 text-sm my-1" >
{ { $elapsedPretty ( duration ) } }
2022-04-25 17:22:12 -05:00
< span v-if = "!isLocal" class="px-4" > {{ $ bytesPretty ( size ) }} < / span >
2022-03-23 17:59:14 -05:00
< / p >
2022-04-25 17:22:12 -05:00
< p v-if = "numTracks" class="text-gray-300 text-sm my-1" > {{ numTracks }} Tracks < / p >
2022-03-23 17:59:14 -05:00
2022-04-10 20:31:47 -05:00
< div v-if = "!isPodcast && progressPercent > 0" class="px-4 py-2 bg-primary text-sm font-semibold rounded-md text-gray-200 mt-4 relative" :class="resettingProgress ? 'opacity-25' : ''" >
2022-03-23 17:59:14 -05:00
< p class = "leading-6" > Your Progress : { { Math . round ( progressPercent * 100 ) } } % < / p >
< p v-if = "progressPercent < 1" class="text-gray-400 text-xs" > {{ $ elapsedPretty ( userTimeRemaining ) }} remaining < / p >
< div v-if = "!resettingProgress" class="absolute -top-1.5 -right-1.5 p-1 w-5 h-5 rounded-full bg-bg hover:bg-error border border-primary flex items-center justify-center cursor-pointer" @click.stop="clearProgressClick" >
< span class = "material-icons text-sm" > close < / span >
< / div >
< / div >
2022-04-03 17:07:26 -05:00
< div v-if = "isLocal" class="flex mt-4 -mr-2" >
< ui-btn color = "success" :disabled = "isPlaying" class = "flex items-center justify-center flex-grow mr-2" :padding-x = "4" @click ="playClick" >
< span v-show = "!isPlaying" class="material-icons" > play_arrow < / span >
< span class = "px-1 text-sm" > { { isPlaying ? 'Playing' : 'Play Local' } } < / span >
< / ui-btn >
< ui-btn v-if = "showRead && isConnected" color="info" class="flex items-center justify-center mr-2" :class="showPlay ? '' : 'flex-grow'" :padding-x="2" @click="readBook" >
< span class = "material-icons" > auto _stories < / span >
< span v-if = "!showPlay" class="px-2 text-base" > Read {{ ebookFormat }} < / span >
< / ui-btn >
2022-04-27 15:19:29 -04:00
< ui-read-icon-btn :disabled = "isProcessingReadUpdate" :is-read = "userIsFinished" class = "mx-0.5 flex items-center justify-center" @click ="toggleFinished" / >
2022-04-03 17:07:26 -05:00
< / div >
2022-04-07 18:46:58 -05:00
< div v -else -if = " ( user & & ( showPlay | | showRead ) ) | | hasLocal " class = "flex mt-4 -mr-2" >
2022-03-23 17:59:14 -05:00
< ui-btn v-if = "showPlay" color="success" :disabled="isPlaying" class="flex items-center justify-center flex-grow mr-2" :padding-x="4" @click="playClick" >
< span v-show = "!isPlaying" class="material-icons" > play_arrow < / span >
2022-04-07 18:46:58 -05:00
< span class = "px-1 text-sm" > { { isPlaying ? ( isStreaming ? 'Streaming' : 'Playing' ) : hasLocal ? 'Play Local' : 'Play Stream' } } < / span >
2022-03-23 17:59:14 -05:00
< / ui-btn >
2022-04-07 18:46:58 -05:00
< ui-btn v-if = "showRead && user" color="info" class="flex items-center justify-center mr-2" :class="showPlay ? '' : 'flex-grow'" :padding-x="2" @click="readBook" >
2022-03-23 17:59:14 -05:00
< span class = "material-icons" > auto _stories < / span >
< span v-if = "!showPlay" class="px-2 text-base" > Read {{ ebookFormat }} < / span >
< / ui-btn >
2022-04-07 18:46:58 -05:00
< ui-btn v-if = "user && showPlay && !isIos && !hasLocal" :color="downloadItem ? 'warning' : 'primary'" class="flex items-center justify-center" :padding-x="2" @click="downloadClick" >
< span class = "material-icons" : class = "downloadItem ? 'animate-pulse' : ''" > { { downloadItem ? 'downloading' : 'download' } } < / span >
2022-03-23 17:59:14 -05:00
< / ui-btn >
2022-04-27 15:19:29 -04:00
< ui-read-icon-btn :disabled = "isProcessingReadUpdate" :is-read = "userIsFinished" class = "mx-0.5 flex items-center justify-center" @click ="toggleFinished" / >
2022-03-23 17:59:14 -05:00
< / div >
< / div >
< / div >
2022-04-07 18:46:58 -05:00
< div v-if = "downloadItem" class="py-3" >
< p class = "text-center text-lg" > Downloading ! ( { { Math . round ( downloadItem . itemProgress * 100 ) } } % ) < / p >
< / div >
2022-03-23 17:59:14 -05:00
< div class = "w-full py-4" >
2022-04-12 18:40:35 -05:00
< p class = "text-sm" > { { description } } < / p >
2022-03-23 17:59:14 -05:00
< / div >
2022-04-02 19:43:43 -05:00
2022-04-15 20:48:39 -05:00
< tables-podcast-episodes-table v-if = "isPodcast" :library-item-id="libraryItemId" :local-library-item-id="localLibraryItemId" :episodes="episodes" :local-episodes="localLibraryItemEpisodes" :is-local="isLocal" / >
2022-04-10 20:31:47 -05:00
2022-04-02 19:43:43 -05:00
< modals-select-local-folder-modal v-model = "showSelectLocalFolder" :media-type="mediaType" @select="selectedLocalFolder" / >
2022-03-23 17:59:14 -05:00
< / div >
< / template >
< script >
import { Dialog } from '@capacitor/dialog'
2022-04-04 19:08:27 -05:00
import { AbsFileSystem , AbsDownloader } from '@/plugins/capacitor'
2022-03-23 17:59:14 -05:00
export default {
async asyncData ( { store , params , redirect , app } ) {
var libraryItemId = params . id
var libraryItem = null
2022-04-08 18:07:31 -05:00
console . log ( libraryItemId )
2022-04-03 17:07:26 -05:00
if ( libraryItemId . startsWith ( 'local' ) ) {
libraryItem = await app . $db . getLocalLibraryItem ( libraryItemId )
2022-04-08 18:07:31 -05:00
console . log ( 'Got lli' , libraryItem )
2022-04-03 17:07:26 -05:00
} else if ( store . state . user . serverConnectionConfig ) {
2022-03-23 17:59:14 -05:00
libraryItem = await app . $axios . $get ( ` /api/items/ ${ libraryItemId } ?expanded=1 ` ) . catch ( ( error ) => {
console . error ( 'Failed' , error )
return false
} )
2022-04-07 18:46:58 -05:00
// Check if
if ( libraryItem ) {
var localLibraryItem = await app . $db . getLocalLibraryItemByLLId ( libraryItemId )
if ( localLibraryItem ) {
console . log ( 'Library item has local library item also' , localLibraryItem . id )
libraryItem . localLibraryItem = localLibraryItem
}
}
2022-03-23 17:59:14 -05:00
}
if ( ! libraryItem ) {
console . error ( 'No item...' , params . id )
return redirect ( '/' )
}
return {
libraryItem
}
} ,
data ( ) {
return {
2022-04-02 19:43:43 -05:00
resettingProgress : false ,
2022-04-23 00:27:03 -04:00
isProcessingReadUpdate : false ,
2022-04-02 19:43:43 -05:00
showSelectLocalFolder : false
2022-03-23 17:59:14 -05:00
}
} ,
computed : {
isIos ( ) {
return this . $platform === 'ios'
} ,
2022-04-03 17:07:26 -05:00
isLocal ( ) {
return this . libraryItem . isLocal
} ,
2022-04-07 18:46:58 -05:00
hasLocal ( ) {
// Server library item has matching local library item
return this . isLocal || this . libraryItem . localLibraryItem
} ,
localLibraryItem ( ) {
if ( this . isLocal ) return this . libraryItem
return this . libraryItem . localLibraryItem || null
} ,
2022-04-15 20:48:39 -05:00
localLibraryItemId ( ) {
return this . localLibraryItem ? this . localLibraryItem . id : null
} ,
localLibraryItemEpisodes ( ) {
if ( ! this . isPodcast || ! this . localLibraryItem ) return [ ]
var podcastMedia = this . localLibraryItem . media
return podcastMedia ? podcastMedia . episodes || [ ] : [ ]
} ,
2022-03-23 17:59:14 -05:00
isConnected ( ) {
return this . $store . state . socketConnected
} ,
bookCoverAspectRatio ( ) {
return this . $store . getters [ 'getBookCoverAspectRatio' ]
} ,
libraryItemId ( ) {
return this . libraryItem . id
} ,
2022-04-02 19:43:43 -05:00
mediaType ( ) {
return this . libraryItem . mediaType
} ,
2022-04-10 20:31:47 -05:00
isPodcast ( ) {
return this . mediaType == 'podcast'
} ,
2022-03-23 17:59:14 -05:00
media ( ) {
return this . libraryItem . media || { }
} ,
mediaMetadata ( ) {
return this . media . metadata || { }
} ,
title ( ) {
return this . mediaMetadata . title
} ,
author ( ) {
2022-04-10 20:31:47 -05:00
if ( this . isPodcast ) return this . mediaMetadata . author
2022-03-23 17:59:14 -05:00
return this . mediaMetadata . authorName
} ,
description ( ) {
return this . mediaMetadata . description || ''
} ,
series ( ) {
return this . mediaMetadata . series || [ ]
} ,
duration ( ) {
2022-03-28 19:53:53 -05:00
return this . media . duration
2022-03-23 17:59:14 -05:00
} ,
size ( ) {
2022-03-28 19:53:53 -05:00
return this . media . size
2022-03-23 17:59:14 -05:00
} ,
2022-04-07 18:46:58 -05:00
user ( ) {
return this . $store . state . user . user
} ,
2022-03-23 17:59:14 -05:00
userToken ( ) {
return this . $store . getters [ 'user/getToken' ]
} ,
userItemProgress ( ) {
2022-04-09 12:03:37 -05:00
if ( this . isLocal ) return this . $store . getters [ 'globals/getLocalMediaProgressById' ] ( this . libraryItemId )
2022-04-08 19:05:32 -05:00
return this . $store . getters [ 'user/getUserMediaProgress' ] ( this . libraryItemId )
2022-03-23 17:59:14 -05:00
} ,
userIsFinished ( ) {
return this . userItemProgress ? ! ! this . userItemProgress . isFinished : false
} ,
userTimeRemaining ( ) {
if ( ! this . userItemProgress ) return 0
var duration = this . userItemProgress . duration || this . duration
return duration - this . userItemProgress . currentTime
} ,
progressPercent ( ) {
return this . userItemProgress ? Math . max ( Math . min ( 1 , this . userItemProgress . progress ) , 0 ) : 0
} ,
userProgressStartedAt ( ) {
return this . userItemProgress ? this . userItemProgress . startedAt : 0
} ,
userProgressFinishedAt ( ) {
return this . userItemProgress ? this . userItemProgress . finishedAt : 0
} ,
isStreaming ( ) {
2022-04-10 20:31:47 -05:00
return this . isPlaying && ! this . $store . state . playerIsLocal
2022-03-23 17:59:14 -05:00
} ,
isPlaying ( ) {
2022-04-10 20:31:47 -05:00
return this . $store . getters [ 'getIsItemStreaming' ] ( this . libraryItemId )
2022-03-23 17:59:14 -05:00
} ,
numTracks ( ) {
2022-03-28 19:53:53 -05:00
if ( ! this . media . tracks ) return 0
return this . media . tracks . length || 0
2022-03-23 17:59:14 -05:00
} ,
isMissing ( ) {
return this . libraryItem . isMissing
} ,
isIncomplete ( ) {
return this . libraryItem . isIncomplete
} ,
showPlay ( ) {
2022-03-28 19:53:53 -05:00
return ! this . isMissing && ! this . isIncomplete && this . numTracks
2022-03-23 17:59:14 -05:00
} ,
showRead ( ) {
2022-04-16 06:10:10 -05:00
return this . ebookFile && this . ebookFormat !== 'pdf'
2022-03-23 17:59:14 -05:00
} ,
2022-03-28 19:53:53 -05:00
ebookFile ( ) {
return this . media . ebookFile
2022-03-23 17:59:14 -05:00
} ,
ebookFormat ( ) {
2022-03-28 19:53:53 -05:00
if ( ! this . ebookFile ) return null
return this . ebookFile . ebookFormat
2022-03-23 17:59:14 -05:00
} ,
hasStoragePermission ( ) {
return this . $store . state . hasStoragePermission
2022-04-07 18:46:58 -05:00
} ,
downloadItem ( ) {
return this . $store . getters [ 'globals/getDownloadItem' ] ( this . libraryItemId )
} ,
2022-04-10 20:31:47 -05:00
episodes ( ) {
return this . media . episodes || [ ]
2022-03-23 17:59:14 -05:00
}
} ,
methods : {
readBook ( ) {
this . $store . commit ( 'openReader' , this . libraryItem )
} ,
playClick ( ) {
2022-04-07 18:46:58 -05:00
// Todo: Allow playing local or streaming
2022-04-10 20:31:47 -05:00
if ( this . hasLocal ) return this . $eventBus . $emit ( 'play-item' , { libraryItemId : this . localLibraryItem . id } )
this . $eventBus . $emit ( 'play-item' , { libraryItemId : this . libraryItemId } )
2022-03-23 17:59:14 -05:00
} ,
async clearProgressClick ( ) {
const { value } = await Dialog . confirm ( {
title : 'Confirm' ,
message : 'Are you sure you want to reset your progress?'
} )
if ( value ) {
this . resettingProgress = true
2022-04-09 12:03:37 -05:00
if ( this . isLocal ) {
2022-04-09 20:29:59 -05:00
// TODO: If connected to server also sync with server
2022-04-09 12:03:37 -05:00
await this . $db . removeLocalMediaProgress ( this . libraryItemId )
this . $store . commit ( 'globals/removeLocalMediaProgress' , this . libraryItemId )
} else {
2022-04-09 20:29:59 -05:00
var progressId = this . userItemProgress . id
2022-04-09 12:03:37 -05:00
await this . $axios
. $delete ( ` /api/me/progress/ ${ this . libraryItemId } ` )
. then ( ( ) => {
console . log ( 'Progress reset complete' )
this . $toast . success ( ` Your progress was reset ` )
2022-04-09 20:29:59 -05:00
this . $store . commit ( 'user/removeMediaProgress' , progressId )
2022-04-09 12:03:37 -05:00
} )
. catch ( ( error ) => {
console . error ( 'Progress reset failed' , error )
} )
}
this . resettingProgress = false
2022-03-23 17:59:14 -05:00
}
} ,
itemUpdated ( libraryItem ) {
if ( libraryItem . id === this . libraryItemId ) {
console . log ( 'Item Updated' )
this . libraryItem = libraryItem
}
} ,
2022-04-02 19:43:43 -05:00
async selectFolder ( ) {
// Select and save the local folder for media type
2022-04-04 19:08:27 -05:00
var folderObj = await AbsFileSystem . selectFolder ( { mediaType : this . mediaType } )
2022-04-02 19:43:43 -05:00
if ( folderObj . error ) {
return this . $toast . error ( ` Error: ${ folderObj . error || 'Unknown Error' } ` )
}
return folderObj
} ,
selectedLocalFolder ( localFolder ) {
this . showSelectLocalFolder = false
this . download ( localFolder )
} ,
2022-03-23 17:59:14 -05:00
downloadClick ( ) {
2022-04-07 18:46:58 -05:00
if ( this . downloadItem ) {
return
}
2022-04-02 19:43:43 -05:00
this . download ( )
} ,
async download ( selectedLocalFolder = null ) {
2022-04-03 17:07:26 -05:00
if ( ! this . numTracks ) {
2022-04-02 19:43:43 -05:00
return
}
// Get the local folder to download to
var localFolder = selectedLocalFolder
if ( ! localFolder ) {
2022-04-03 14:24:17 -05:00
var localFolders = ( await this . $db . getLocalFolders ( ) ) || [ ]
2022-04-02 19:43:43 -05:00
console . log ( 'Local folders loaded' , localFolders . length )
var foldersWithMediaType = localFolders . filter ( ( lf ) => {
console . log ( 'Checking local folder' , lf . mediaType )
return lf . mediaType == this . mediaType
} )
console . log ( 'Folders with media type' , this . mediaType , foldersWithMediaType . length )
if ( ! foldersWithMediaType . length ) {
// No local folders or no local folders with this media type
localFolder = await this . selectFolder ( )
} else if ( foldersWithMediaType . length == 1 ) {
console . log ( 'Only 1 local folder with this media type - auto select it' )
localFolder = foldersWithMediaType [ 0 ]
} else {
console . log ( 'Multiple folders with media type' )
this . showSelectLocalFolder = true
return
}
if ( ! localFolder ) {
return this . $toast . error ( 'Invalid download folder' )
}
}
console . log ( 'Local folder' , JSON . stringify ( localFolder ) )
var startDownloadMessage = ` Start download for " ${ this . title } " with ${ this . numTracks } audio track ${ this . numTracks == 1 ? '' : 's' } to folder ${ localFolder . name } ? `
const { value } = await Dialog . confirm ( {
title : 'Confirm' ,
message : startDownloadMessage
} )
if ( value ) {
this . startDownload ( localFolder )
}
} ,
async startDownload ( localFolder ) {
console . log ( 'Starting download to local folder' , localFolder . name )
2022-04-04 19:08:27 -05:00
var downloadRes = await AbsDownloader . downloadLibraryItem ( { libraryItemId : this . libraryItemId , localFolderId : localFolder . id } )
2022-04-07 18:46:58 -05:00
if ( downloadRes && downloadRes . error ) {
2022-04-02 19:43:43 -05:00
var errorMsg = downloadRes . error || 'Unknown error'
console . error ( 'Download error' , errorMsg )
2022-04-03 19:16:17 -05:00
this . $toast . error ( errorMsg )
2022-03-23 17:59:14 -05:00
}
2022-04-07 18:46:58 -05:00
} ,
newLocalLibraryItem ( item ) {
if ( item . libraryItemId == this . libraryItemId ) {
console . log ( 'New local library item' , item . id )
this . $set ( this . libraryItem , 'localLibraryItem' , item )
}
2022-04-23 00:27:03 -04:00
} ,
2022-04-24 22:02:22 -04:00
async toggleFinished ( ) {
2022-04-23 00:27:03 -04:00
this . isProcessingReadUpdate = true
2022-04-24 22:02:22 -04:00
if ( this . isLocal || this . localEpisode ) {
var isFinished = ! this . userIsFinished
var localMediaProgressId = this . localLibraryItemId
console . log ( 'toggleFinished local media progress id' , localMediaProgressId , isFinished )
var payload = await this . $db . updateLocalMediaProgressFinished ( { localMediaProgressId , isFinished } )
console . log ( 'toggleFinished payload' , JSON . stringify ( payload ) )
if ( ! payload || payload . error ) {
var errorMsg = payload ? payload . error : 'Unknown error'
this . $toast . error ( errorMsg )
} else {
var localMediaProgress = payload . localMediaProgress
console . log ( 'toggleFinished localMediaProgress' , JSON . stringify ( localMediaProgress ) )
if ( localMediaProgress ) {
this . $store . commit ( 'globals/updateLocalMediaProgress' , localMediaProgress )
}
var lmp = this . $store . getters [ 'globals/getLocalMediaProgressById' ] ( this . libraryItemId )
console . log ( 'toggleFinished Check LMP' , this . libraryItemId , JSON . stringify ( lmp ) )
var serverUpdated = payload . server
if ( serverUpdated ) {
this . $toast . success ( ` Local & Server Item marked as ${ isFinished ? 'Finished' : 'Not Finished' } ` )
} else {
this . $toast . success ( ` Local Item marked as ${ isFinished ? 'Finished' : 'Not Finished' } ` )
}
}
this . isProcessingReadUpdate = false
} else {
var updatePayload = {
isFinished : ! this . userIsFinished
}
this . $axios
. $patch ( ` /api/me/progress/ ${ this . libraryItemId } ` , updatePayload )
. then ( ( ) => {
this . isProcessingReadUpdate = false
this . $toast . success ( ` Item marked as ${ updatePayload . isFinished ? 'Finished' : 'Not Finished' } ` )
} )
. catch ( ( error ) => {
console . error ( 'Failed' , error )
this . isProcessingReadUpdate = false
this . $toast . error ( ` Failed to mark as ${ updatePayload . isFinished ? 'Finished' : 'Not Finished' } ` )
} )
}
}
2022-03-23 17:59:14 -05:00
} ,
mounted ( ) {
2022-04-07 18:46:58 -05:00
this . $eventBus . $on ( 'new-local-library-item' , this . newLocalLibraryItem )
2022-04-03 14:24:17 -05:00
// this.$server.socket.on('item_updated', this.itemUpdated)
2022-03-23 17:59:14 -05:00
} ,
beforeDestroy ( ) {
2022-04-07 18:46:58 -05:00
this . $eventBus . $off ( 'new-local-library-item' , this . newLocalLibraryItem )
2022-04-03 14:24:17 -05:00
// this.$server.socket.off('item_updated', this.itemUpdated)
2022-03-23 17:59:14 -05:00
}
}
< / script >