Add local library items to bookshelf and landing page

This commit is contained in:
advplyr 2022-04-03 17:07:26 -05:00
parent 4f8b13b23d
commit 9fd3dc6978
15 changed files with 279 additions and 125 deletions

View file

@ -141,9 +141,9 @@ class AudioDownloader : Plugin() {
var downloadItem = DownloadItem(libraryItem.id, localFolder, bookTitle, mutableListOf())
var itemFolderPath = localFolder.absolutePath + "/" + bookTitle
tracks.forEach { audioFile ->
var serverPath = "/s/item/${libraryItem.id}/${cleanRelPath(audioFile.metadata.relPath)}"
var destinationFilename = getFilenameFromRelPath(audioFile.metadata.relPath)
Log.d(tag, "Audio File Server Path $serverPath | AF RelPath ${audioFile.metadata.relPath} | LocalFolder Path ${localFolder.absolutePath} | DestName ${destinationFilename}")
var serverPath = "/s/item/${libraryItem.id}/${cleanRelPath(audioFile.relPath)}"
var destinationFilename = getFilenameFromRelPath(audioFile.relPath)
Log.d(tag, "Audio File Server Path $serverPath | AF RelPath ${audioFile.relPath} | LocalFolder Path ${localFolder.absolutePath} | DestName ${destinationFilename}")
var destinationFile = File("$itemFolderPath/$destinationFilename")
var destinationUri = Uri.fromFile(destinationFile)
var downloadUri = Uri.parse("${DeviceManager.serverAddress}${serverPath}?token=${DeviceManager.token}")

View file

@ -78,6 +78,16 @@ class MyNativeAudio : Plugin() {
var libraryItemId = call.getString("libraryItemId", "").toString()
var playWhenReady = call.getBoolean("playWhenReady") == true
if (libraryItemId.startsWith("local")) { // Play local media item
DeviceManager.dbManager.getLocalMediaItem(libraryItemId)?.let {
Handler(Looper.getMainLooper()).post() {
Log.d(tag, "Preparing Local Media item ${jacksonObjectMapper().writeValueAsString(it)}")
var playbackSession = it.getPlaybackSession()
playerNotificationService.preparePlayer(playbackSession, playWhenReady)
}
return call.resolve(JSObject())
}
} else { // Play library item from server
apiHandler.playLibraryItem(libraryItemId, false) {
Handler(Looper.getMainLooper()).post() {
@ -88,24 +98,6 @@ class MyNativeAudio : Plugin() {
call.resolve(JSObject(jacksonObjectMapper().writeValueAsString(it)))
}
}
@PluginMethod
fun playLocalLibraryItem(call:PluginCall) {
var localMediaItemId = call.getString("localMediaItemId", "").toString()
var playWhenReady = call.getBoolean("playWhenReady") == true
Log.d(tag, "playLocalLibraryItem $playWhenReady")
DeviceManager.dbManager.loadLocalMediaItem(localMediaItemId)?.let {
Handler(Looper.getMainLooper()).post() {
Log.d(tag, "Preparing Local Media item ${jacksonObjectMapper().writeValueAsString(it)}")
var playbackSession = it.getPlaybackSession()
playerNotificationService.preparePlayer(playbackSession, playWhenReady)
}
return call.resolve(JSObject())
}
var errObj = JSObject()
errObj.put("error", "Item Not Found")
call.resolve(errObj)
}
@PluginMethod

View file

@ -1,6 +1,6 @@
package com.audiobookshelf.app.data
import android.net.Uri
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
@JsonIgnoreProperties(ignoreUnknown = true)
@ -24,8 +24,16 @@ data class AudioProbeChapter(
val id:Int,
val start:Int,
val end:Int,
val tags:AudioProbeChapterTags
)
val tags:AudioProbeChapterTags?
) {
@JsonIgnore
fun getBookChapter():BookChapter {
var startS = start / 1000.0
var endS = end / 1000.0
var title = tags?.title ?: "Chapter $id"
return BookChapter(id, startS, endS, title)
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
data class AudioProbeFormatTags(
@ -57,4 +65,10 @@ class AudioProbeResult (
val size get() = format.size
val title get() = format.tags.title ?: format.filename.split("/").last()
val artist get() = format.tags.artist ?: ""
@JsonIgnore
fun getBookChapters(): List<BookChapter> {
if (chapters.isEmpty()) return mutableListOf()
return chapters.map { it.getBookChapter() }
}
}

View file

@ -45,10 +45,10 @@ data class Podcast(
data class Book(
var metadata:BookMetadata,
var coverPath:String?,
var tags:MutableList<String>,
var audioFiles:MutableList<AudioFile>,
var chapters:MutableList<BookChapter>,
var tracks:MutableList<AudioFile>?,
var tags:List<String>,
var audioFiles:List<AudioFile>,
var chapters:List<BookChapter>,
var tracks:List<AudioTrack>?,
var size:Long?,
var duration:Double?
) : MediaType()
@ -154,9 +154,10 @@ data class AudioTrack(
var title:String,
var contentUrl:String,
var mimeType:String,
var metadata:FileMetadata?,
var isLocal:Boolean,
var localFileId:String?,
var audioProbeResult:AudioProbeResult?
var audioProbeResult:AudioProbeResult?,
) {
@get:JsonIgnore
@ -165,6 +166,13 @@ data class AudioTrack(
val durationMs get() = (duration * 1000L).toLong()
@get:JsonIgnore
val endOffsetMs get() = startOffsetMs + durationMs
@get:JsonIgnore
val relPath get() = metadata?.relPath ?: ""
@JsonIgnore
fun getBookChapter():BookChapter {
return BookChapter(index + 1,startOffset, startOffset + duration, title)
}
}
@JsonIgnoreProperties(ignoreUnknown = true)

View file

@ -56,7 +56,7 @@ class DbManager : Plugin() {
}
}
fun loadLocalMediaItem(localMediaItemId:String):LocalMediaItem? {
fun getLocalMediaItem(localMediaItemId:String):LocalMediaItem? {
return Paper.book("localMediaItems").read(localMediaItemId)
}
@ -153,6 +153,32 @@ class DbManager : Plugin() {
}
}
@PluginMethod
fun getLocalLibraryItems_WV(call:PluginCall) {
GlobalScope.launch(Dispatchers.IO) {
var localLibraryItems = getLocalMediaItems().map {
it.getLocalLibraryItem()
}
var jsobj = JSObject()
jsobj.put("localLibraryItems", jacksonObjectMapper().writeValueAsString(localLibraryItems))
call.resolve(jsobj)
}
}
@PluginMethod
fun getLocalLibraryItem_WV(call:PluginCall) {
var id = call.getString("id", "").toString()
GlobalScope.launch(Dispatchers.IO) {
var mediaItem = getLocalMediaItem(id)
var localLibraryItem = mediaItem?.getLocalLibraryItem()
if (localLibraryItem == null) {
call.resolve()
} else {
call.resolve(JSObject(jacksonObjectMapper().writeValueAsString(localLibraryItem)))
}
}
}
@PluginMethod
fun setCurrentServerConnectionConfig_WV(call:PluginCall) {
var serverConnectionConfigId = call.getString("id", "").toString()

View file

@ -18,6 +18,18 @@ data class DeviceData(
var lastServerConnectionConfigId:String?
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class LocalLibraryItem(
var id:String,
var folderId:String,
var absolutePath:String,
var isInvalid:Boolean,
var mediaType:String,
var media:MediaType,
var localFiles:MutableList<LocalFile>,
var isLocal:Boolean
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class LocalMediaItem(
var id:String,
@ -40,6 +52,13 @@ data class LocalMediaItem(
return total
}
@JsonIgnore
fun getTotalSize():Long {
var total = 0L
localFiles.forEach { total += it.size }
return total
}
@JsonIgnore
fun getMediaMetadata():MediaTypeMetadata {
return if (mediaType == "book") {
@ -54,7 +73,36 @@ data class LocalMediaItem(
var sessionId = "play-${UUID.randomUUID()}"
var mediaMetadata = getMediaMetadata()
return PlaybackSession(sessionId,null,null,null, mediaType, mediaMetadata, mutableListOf(), name, "author name here",null,getDuration(),PLAYMETHOD_LOCAL,audioTracks,0.0,null,this,null,null)
var chapters = getAudiobookChapters()
var authorName = "Unknown"
if (mediaType == "book") {
var bookMetadata = mediaMetadata as BookMetadata
authorName = bookMetadata?.authorName ?: "Unknown"
}
return PlaybackSession(sessionId,null,null,null, mediaType, mediaMetadata, chapters, name, authorName,null,getDuration(),PLAYMETHOD_LOCAL,audioTracks,0.0,null,this,null,null)
}
@JsonIgnore
fun getAudiobookChapters():List<BookChapter> {
if (mediaType != "book" || audioTracks.isEmpty()) return mutableListOf()
if (audioTracks.size == 1) { // Single track audiobook look for chapters from ffprobe
return audioTracks[0].audioProbeResult?.getBookChapters() ?: mutableListOf()
}
// Multi-track make chapters from tracks
return audioTracks.map { it.getBookChapter() }
}
@JsonIgnore
fun getLocalLibraryItem():LocalLibraryItem {
var mediaMetadata = getMediaMetadata()
if (mediaType == "book") {
var chapters = getAudiobookChapters()
var book = Book(mediaMetadata as BookMetadata, coverContentUrl, mutableListOf(), mutableListOf(), chapters,audioTracks,getTotalSize(),getDuration())
return LocalLibraryItem(id, folderId, absolutePath, false,mediaType, book, localFiles, true)
} else {
var podcast = Podcast(mediaMetadata as PodcastMetadata, coverContentUrl, mutableListOf(), mutableListOf(), false)
return LocalLibraryItem(id, folderId, absolutePath, false, mediaType, podcast,localFiles,true)
}
}
}

View file

@ -23,7 +23,7 @@ class PlaybackSession(
var episodeId:String?,
var mediaType:String,
var mediaMetadata:MediaTypeMetadata,
var chapters:MutableList<BookChapter>,
var chapters:List<BookChapter>,
var displayTitle: String?,
var displayAuthor: String?,
var coverPath:String?,

View file

@ -11,9 +11,6 @@ import com.arthenica.ffmpegkit.Level
import com.audiobookshelf.app.data.*
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class FolderScanner(var ctx: Context) {
private val tag = "FolderScanner"
@ -61,7 +58,7 @@ class FolderScanner(var ctx: Context) {
Log.d(tag, "Iterating over Folder Found ${it.name} | ${it.getSimplePath(ctx)} | URI: ${it.uri}")
var itemFolderName = it.name ?: ""
var itemId = DeviceManager.getBase64Id(it.id)
var itemId = "local_" + DeviceManager.getBase64Id(it.id)
var existingMediaItem = existingMediaItems.find { emi -> emi.id == itemId }
var existingLocalFiles = existingMediaItem?.localFiles ?: mutableListOf()
@ -130,7 +127,7 @@ class FolderScanner(var ctx: Context) {
audioTrackToAdd = existingAudioTrack
} else {
// Create new audio track
var track = AudioTrack(index, startOffset, audioProbeResult.duration, filename, localFile.contentUrl, mimeType, true, localFileId, audioProbeResult)
var track = AudioTrack(index, startOffset, audioProbeResult.duration, filename, localFile.contentUrl, mimeType, null, true, localFileId, audioProbeResult)
audioTrackToAdd = track
}

View file

@ -175,15 +175,6 @@ export default {
.catch((error) => {
console.error('TEST failed', error)
})
},
async playLocalItem(localMediaItemId) {
MyNativeAudio.playLocalLibraryItem({ localMediaItemId, playWhenReady: true })
.then((data) => {
console.log('TEST library item play response', JSON.stringify(data))
})
.catch((error) => {
console.error('TEST failed', error)
})
}
},
mounted() {
@ -195,7 +186,6 @@ export default {
this.setListeners()
this.$eventBus.$on('play-item', this.playLibraryItem)
this.$eventBus.$on('play-local-item', this.playLocalItem)
this.$eventBus.$on('close-stream', this.closeStreamOnly)
this.$store.commit('user/addSettingsListener', { id: 'streamContainer', meth: this.settingsUpdated })
},
@ -211,7 +201,6 @@ export default {
// this.$server.socket.off('stream_reset', this.streamReset)
// }
this.$eventBus.$off('play-item', this.playLibraryItem)
this.$eventBus.$off('play-local-item', this.playLocalItem)
this.$eventBus.$off('close-stream', this.closeStreamOnly)
this.$store.commit('user/removeSettingsListener', 'streamContainer')
}

View file

@ -17,11 +17,11 @@
<div v-if="booksInSeries" class="absolute z-20 top-1.5 right-1.5 rounded-md leading-3 text-sm p-1 font-semibold text-white flex items-center justify-center" style="background-color: #cd9d49dd">{{ booksInSeries }}</div>
<div class="w-full h-full absolute top-0 left-0 rounded overflow-hidden z-10">
<div v-show="audiobook && !imageReady" class="absolute top-0 left-0 w-full h-full flex items-center justify-center" :style="{ padding: sizeMultiplier * 0.5 + 'rem' }">
<div v-show="libraryItem && !imageReady" class="absolute top-0 left-0 w-full h-full flex items-center justify-center" :style="{ padding: sizeMultiplier * 0.5 + 'rem' }">
<p :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }" class="font-book text-gray-300 text-center">{{ title }}</p>
</div>
<img v-show="audiobook" ref="cover" :src="bookCoverSrc" class="w-full h-full transition-opacity duration-300" :class="showCoverBg ? 'object-contain' : 'object-fill'" @load="imageLoaded" :style="{ opacity: imageReady ? 1 : 0 }" />
<img v-show="libraryItem" ref="cover" :src="bookCoverSrc" class="w-full h-full transition-opacity duration-300" :class="showCoverBg ? 'object-contain' : 'object-fill'" @load="imageLoaded" :style="{ opacity: imageReady ? 1 : 0 }" />
<!-- Placeholder Cover Title & Author -->
<div v-if="!hasCover" class="absolute top-0 left-0 right-0 bottom-0 w-full h-full flex items-center justify-center" :style="{ padding: placeholderCoverPadding + 'rem' }">
@ -52,6 +52,8 @@
</template>
<script>
import { Capacitor } from '@capacitor/core'
export default {
props: {
index: Number,
@ -78,7 +80,7 @@ export default {
data() {
return {
isProcessingReadUpdate: false,
audiobook: null,
libraryItem: null,
imageReady: false,
rescanning: false,
selected: false,
@ -90,7 +92,7 @@ export default {
bookMount: {
handler(newVal) {
if (newVal) {
this.audiobook = newVal
this.libraryItem = newVal
}
}
}
@ -100,7 +102,11 @@ export default {
return this.store.state.showExperimentalFeatures
},
_libraryItem() {
return this.audiobook || {}
return this.libraryItem || {}
},
isLocal() {
// Is local library item
return !!this._libraryItem.isLocal
},
media() {
return this._libraryItem.media || {}
@ -112,6 +118,10 @@ export default {
return '/book_placeholder.jpg'
},
bookCoverSrc() {
if (this.isLocal) {
if (this.media.coverPath) return Capacitor.convertFileSrc(this.media.coverPath)
return this.placeholderUrl
}
return this.store.getters['globals/getLibraryItemCoverSrc'](this._libraryItem, this.placeholderUrl)
},
libraryItemId() {
@ -225,8 +235,8 @@ export default {
return this._libraryItem.hasInvalidParts
},
errorText() {
if (this.isMissing) return 'Audiobook directory is missing!'
else if (this.isInvalid) return 'Audiobook has no audio tracks & ebook'
if (this.isMissing) return 'Item directory is missing!'
else if (this.isInvalid) return 'Item has no media files'
var txt = ''
if (this.hasMissingParts) {
txt = `${this.hasMissingParts} missing parts.`
@ -307,7 +317,7 @@ export default {
if (!val) this.selected = false
},
setEntity(libraryItem) {
this.audiobook = libraryItem
this.libraryItem = libraryItem
},
clickCard(e) {
if (this.isSelectionMode) {
@ -323,7 +333,7 @@ export default {
}
},
editClick() {
this.$emit('edit', this.audiobook)
this.$emit('edit', this.libraryItem)
},
toggleFinished() {
var updatePayload = {
@ -369,27 +379,27 @@ export default {
},
showEditModalTracks() {
// More menu func
this.store.commit('showEditModalOnTab', { libraryItem: this.audiobook, tab: 'tracks' })
this.store.commit('showEditModalOnTab', { libraryItem: this.libraryItem, tab: 'tracks' })
},
showEditModalMatch() {
// More menu func
this.store.commit('showEditModalOnTab', { libraryItem: this.audiobook, tab: 'match' })
this.store.commit('showEditModalOnTab', { libraryItem: this.libraryItem, tab: 'match' })
},
showEditModalDownload() {
// More menu func
this.store.commit('showEditModalOnTab', { libraryItem: this.audiobook, tab: 'download' })
this.store.commit('showEditModalOnTab', { libraryItem: this.libraryItem, tab: 'download' })
},
openCollections() {
this.store.commit('setSelectedLibraryItem', this.audiobook)
this.store.commit('setSelectedLibraryItem', this.libraryItem)
this.store.commit('globals/setShowUserCollectionsModal', true)
},
clickReadEBook() {
this.store.commit('showEReader', this.audiobook)
this.store.commit('showEReader', this.libraryItem)
},
selectBtnClick() {
if (this.processingBatch) return
this.selected = !this.selected
this.$emit('select', this.audiobook)
this.$emit('select', this.libraryItem)
},
play() {
var eventBus = this.$eventBus || this.$nuxt.$eventBus

View file

@ -33,6 +33,8 @@
</template>
<script>
import { Capacitor } from '@capacitor/core'
export default {
props: {
libraryItem: {
@ -60,6 +62,10 @@ export default {
}
},
computed: {
isLocal() {
if (!this.libraryItem) return false
return this.libraryItem.isLocal
},
squareAspectRatio() {
return this.bookCoverAspectRatio === 1
},
@ -98,6 +104,10 @@ export default {
return '/book_placeholder.jpg'
},
fullCoverUrl() {
if (this.isLocal) {
if (this.hasCover) return Capacitor.convertFileSrc(this.cover)
return this.placeholderUrl
}
if (this.downloadCover) return this.downloadCover
if (!this.libraryItem) return null
var store = this.$store || this.$nuxt.$store

View file

@ -1,8 +1,10 @@
<template>
<div class="w-full h-full min-h-full relative">
<div v-if="!loading" class="w-full">
<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>
<div v-if="!shelves.length" class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
<div>
@ -123,8 +125,48 @@ export default {
}
},
methods: {
async getLocalMediaItemCategories() {
var localMedia = await this.$db.getLocalLibraryItems()
if (!localMedia || !localMedia.length) return []
console.log('Got local library items', localMedia.length)
var categories = []
var books = []
var podcasts = []
localMedia.forEach((item) => {
if (item.mediaType == 'book') {
books.push(item)
} else if (item.mediaType == 'podcast') {
podcasts.push(item)
}
})
if (books.length) {
categories.push({
id: 'local-books',
label: 'Local Books',
type: 'book',
entities: books
})
}
if (podcasts.length) {
categories.push({
id: 'local-podcasts',
label: 'Local Podcasts',
type: 'podcast',
entities: podcasts
})
}
return categories
},
async fetchCategories() {
if (!this.currentLibraryId) return null
this.loading = true
this.shelves = []
var localCategories = await this.getLocalMediaItemCategories()
console.log('Category shelves', localCategories.length)
this.shelves = this.shelves.concat(localCategories)
if (this.user || !this.currentLibraryId) {
var categories = await this.$axios
.$get(`/api/libraries/${this.currentLibraryId}/personalized?minified=1`)
.then((data) => {
@ -134,8 +176,9 @@ export default {
console.error('Failed to fetch categories', error)
return []
})
this.shelves = categories
console.log('Shelves', this.shelves)
this.shelves = this.shelves.concat(categories)
}
this.loading = false
},
// async socketInit(isConnected) {
// if (isConnected && this.currentLibraryId) {
@ -150,15 +193,13 @@ export default {
async libraryChanged(libid) {
if (this.isSocketConnected && this.currentLibraryId) {
await this.fetchCategories()
} else {
this.shelves = this.downloadOnlyShelves
}
},
downloadsLoaded() {
if (!this.isSocketConnected) {
this.shelves = this.downloadOnlyShelves
}
},
// downloadsLoaded() {
// if (!this.isSocketConnected) {
// this.shelves = this.downloadOnlyShelves
// }
// },
audiobookAdded(audiobook) {
console.log('Audiobook added', audiobook)
// TODO: Check if audiobook would be on this shelf
@ -201,28 +242,15 @@ export default {
}
})
},
audiobookRemoved(audiobook) {
this.removeBookFromShelf(audiobook)
},
audiobooksAdded(audiobooks) {
console.log('audiobooks added', audiobooks)
// TODO: Check if audiobook would be on this shelf
this.fetchCategories()
},
audiobooksUpdated(audiobooks) {
audiobooks.forEach((ab) => {
this.audiobookUpdated(ab)
})
},
initListeners() {
// this.$server.on('initialized', this.socketInit)
this.$eventBus.$on('library-changed', this.libraryChanged)
this.$eventBus.$on('downloads-loaded', this.downloadsLoaded)
// this.$eventBus.$on('downloads-loaded', this.downloadsLoaded)
},
removeListeners() {
// this.$server.off('initialized', this.socketInit)
this.$eventBus.$off('library-changed', this.libraryChanged)
this.$eventBus.$off('downloads-loaded', this.downloadsLoaded)
// this.$eventBus.$off('downloads-loaded', this.downloadsLoaded)
}
},
mounted() {

View file

@ -27,7 +27,21 @@
</div>
</div>
<div v-if="(isConnected && (showPlay || showRead)) || isDownloadPlayable" class="flex mt-4 -mr-2">
<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>
<ui-btn v-if="isConnected && showPlay && !isIos" color="primary" class="flex items-center justify-center" :padding-x="2" @click="downloadClick">
<span class="material-icons">download</span>
<!-- <span class="material-icons" :class="downloadObj ? 'animate-pulse' : ''">{{ downloadObj ? (isDownloading || isDownloadPreparing ? 'downloading' : 'download_done') : 'download' }}</span> -->
</ui-btn>
</div>
<div v-else-if="(isConnected && (showPlay || showRead)) || isDownloadPlayable" class="flex mt-4 -mr-2">
<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>
<span class="px-1 text-sm">{{ isPlaying ? (isStreaming ? 'Streaming' : 'Playing') : isDownloadPlayable ? 'Play local' : 'Play stream' }}</span>
@ -37,7 +51,8 @@
<span v-if="!showPlay" class="px-2 text-base">Read {{ ebookFormat }}</span>
</ui-btn>
<ui-btn v-if="isConnected && showPlay && !isIos" color="primary" class="flex items-center justify-center" :padding-x="2" @click="downloadClick">
<span class="material-icons" :class="downloadObj ? 'animate-pulse' : ''">{{ downloadObj ? (isDownloading || isDownloadPreparing ? 'downloading' : 'download_done') : 'download' }}</span>
<span class="material-icons">download</span>
<!-- <span class="material-icons" :class="downloadObj ? 'animate-pulse' : ''">{{ downloadObj ? (isDownloading || isDownloadPreparing ? 'downloading' : 'download_done') : 'download' }}</span> -->
</ui-btn>
</div>
</div>
@ -61,16 +76,13 @@ export default {
var libraryItemId = params.id
var libraryItem = null
if (store.state.user.serverConnectionConfig) {
if (libraryItemId.startsWith('local')) {
libraryItem = await app.$db.getLocalLibraryItem(libraryItemId)
} else if (store.state.user.serverConnectionConfig) {
libraryItem = await app.$axios.$get(`/api/items/${libraryItemId}?expanded=1`).catch((error) => {
console.error('Failed', error)
return false
})
} else {
var download = store.getters['downloads/getDownload'](libraryItemId)
if (download) {
libraryItem = download.libraryItem
}
}
if (!libraryItem) {
@ -91,6 +103,9 @@ export default {
isIos() {
return this.$platform === 'ios'
},
isLocal() {
return this.libraryItem.isLocal
},
isConnected() {
return this.$store.state.socketConnected
},
@ -182,18 +197,19 @@ export default {
if (!this.ebookFile) return null
return this.ebookFile.ebookFormat
},
isDownloadPreparing() {
return this.downloadObj ? this.downloadObj.isPreparing : false
},
// isDownloadPreparing() {
// return this.downloadObj ? this.downloadObj.isPreparing : false
// },
isDownloadPlayable() {
return this.downloadObj && !this.isDownloading && !this.isDownloadPreparing
},
downloadedCover() {
return this.downloadObj ? this.downloadObj.cover : null
},
downloadObj() {
return this.$store.getters['downloads/getDownload'](this.libraryItemId)
return false
// return this.downloadObj && !this.isDownloading && !this.isDownloadPreparing
},
// downloadedCover() {
// return this.downloadObj ? this.downloadObj.cover : null
// },
// downloadObj() {
// return this.$store.getters['downloads/getDownload'](this.libraryItemId)
// },
hasStoragePermission() {
return this.$store.state.hasStoragePermission
}
@ -264,7 +280,7 @@ export default {
this.download()
},
async download(selectedLocalFolder = null) {
if (!this.numTracks || this.downloadObj) {
if (!this.numTracks) {
return
}

View file

@ -81,7 +81,7 @@ export default {
}
},
play(mediaItem) {
this.$eventBus.$emit('play-local-item', mediaItem.id)
this.$eventBus.$emit('play-item', mediaItem.id)
},
async scanFolder(forceAudioProbe = false) {
this.isScanning = true

View file

@ -87,6 +87,22 @@ class DbService {
return data.localMediaItems
})
}
getLocalLibraryItems() {
if (isWeb) return []
return DbManager.getLocalLibraryItems_WV().then((data) => {
console.log('Loaded all local media items', JSON.stringify(data))
if (data.localLibraryItems && typeof data.localLibraryItems == 'string') {
return JSON.parse(data.localLibraryItems)
}
return data.localLibraryItems
})
}
getLocalLibraryItem(id) {
if (isWeb) return null
return DbManager.getLocalLibraryItem_WV({ id })
}
}
export default ({ app, store }, inject) => {