mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-08-30 14:49:47 +02:00
Add local library items to bookshelf and landing page
This commit is contained in:
parent
4f8b13b23d
commit
9fd3dc6978
15 changed files with 279 additions and 125 deletions
|
@ -141,9 +141,9 @@ class AudioDownloader : Plugin() {
|
||||||
var downloadItem = DownloadItem(libraryItem.id, localFolder, bookTitle, mutableListOf())
|
var downloadItem = DownloadItem(libraryItem.id, localFolder, bookTitle, mutableListOf())
|
||||||
var itemFolderPath = localFolder.absolutePath + "/" + bookTitle
|
var itemFolderPath = localFolder.absolutePath + "/" + bookTitle
|
||||||
tracks.forEach { audioFile ->
|
tracks.forEach { audioFile ->
|
||||||
var serverPath = "/s/item/${libraryItem.id}/${cleanRelPath(audioFile.metadata.relPath)}"
|
var serverPath = "/s/item/${libraryItem.id}/${cleanRelPath(audioFile.relPath)}"
|
||||||
var destinationFilename = getFilenameFromRelPath(audioFile.metadata.relPath)
|
var destinationFilename = getFilenameFromRelPath(audioFile.relPath)
|
||||||
Log.d(tag, "Audio File Server Path $serverPath | AF RelPath ${audioFile.metadata.relPath} | LocalFolder Path ${localFolder.absolutePath} | DestName ${destinationFilename}")
|
Log.d(tag, "Audio File Server Path $serverPath | AF RelPath ${audioFile.relPath} | LocalFolder Path ${localFolder.absolutePath} | DestName ${destinationFilename}")
|
||||||
var destinationFile = File("$itemFolderPath/$destinationFilename")
|
var destinationFile = File("$itemFolderPath/$destinationFilename")
|
||||||
var destinationUri = Uri.fromFile(destinationFile)
|
var destinationUri = Uri.fromFile(destinationFile)
|
||||||
var downloadUri = Uri.parse("${DeviceManager.serverAddress}${serverPath}?token=${DeviceManager.token}")
|
var downloadUri = Uri.parse("${DeviceManager.serverAddress}${serverPath}?token=${DeviceManager.token}")
|
||||||
|
|
|
@ -78,6 +78,16 @@ class MyNativeAudio : Plugin() {
|
||||||
var libraryItemId = call.getString("libraryItemId", "").toString()
|
var libraryItemId = call.getString("libraryItemId", "").toString()
|
||||||
var playWhenReady = call.getBoolean("playWhenReady") == true
|
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) {
|
apiHandler.playLibraryItem(libraryItemId, false) {
|
||||||
|
|
||||||
Handler(Looper.getMainLooper()).post() {
|
Handler(Looper.getMainLooper()).post() {
|
||||||
|
@ -88,24 +98,6 @@ class MyNativeAudio : Plugin() {
|
||||||
call.resolve(JSObject(jacksonObjectMapper().writeValueAsString(it)))
|
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
|
@PluginMethod
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package com.audiobookshelf.app.data
|
package com.audiobookshelf.app.data
|
||||||
|
|
||||||
import android.net.Uri
|
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
@ -24,8 +24,16 @@ data class AudioProbeChapter(
|
||||||
val id:Int,
|
val id:Int,
|
||||||
val start:Int,
|
val start:Int,
|
||||||
val end: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)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
data class AudioProbeFormatTags(
|
data class AudioProbeFormatTags(
|
||||||
|
@ -57,4 +65,10 @@ class AudioProbeResult (
|
||||||
val size get() = format.size
|
val size get() = format.size
|
||||||
val title get() = format.tags.title ?: format.filename.split("/").last()
|
val title get() = format.tags.title ?: format.filename.split("/").last()
|
||||||
val artist get() = format.tags.artist ?: ""
|
val artist get() = format.tags.artist ?: ""
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
fun getBookChapters(): List<BookChapter> {
|
||||||
|
if (chapters.isEmpty()) return mutableListOf()
|
||||||
|
return chapters.map { it.getBookChapter() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,10 +45,10 @@ data class Podcast(
|
||||||
data class Book(
|
data class Book(
|
||||||
var metadata:BookMetadata,
|
var metadata:BookMetadata,
|
||||||
var coverPath:String?,
|
var coverPath:String?,
|
||||||
var tags:MutableList<String>,
|
var tags:List<String>,
|
||||||
var audioFiles:MutableList<AudioFile>,
|
var audioFiles:List<AudioFile>,
|
||||||
var chapters:MutableList<BookChapter>,
|
var chapters:List<BookChapter>,
|
||||||
var tracks:MutableList<AudioFile>?,
|
var tracks:List<AudioTrack>?,
|
||||||
var size:Long?,
|
var size:Long?,
|
||||||
var duration:Double?
|
var duration:Double?
|
||||||
) : MediaType()
|
) : MediaType()
|
||||||
|
@ -154,9 +154,10 @@ data class AudioTrack(
|
||||||
var title:String,
|
var title:String,
|
||||||
var contentUrl:String,
|
var contentUrl:String,
|
||||||
var mimeType:String,
|
var mimeType:String,
|
||||||
|
var metadata:FileMetadata?,
|
||||||
var isLocal:Boolean,
|
var isLocal:Boolean,
|
||||||
var localFileId:String?,
|
var localFileId:String?,
|
||||||
var audioProbeResult:AudioProbeResult?
|
var audioProbeResult:AudioProbeResult?,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
|
@ -165,6 +166,13 @@ data class AudioTrack(
|
||||||
val durationMs get() = (duration * 1000L).toLong()
|
val durationMs get() = (duration * 1000L).toLong()
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val endOffsetMs get() = startOffsetMs + durationMs
|
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)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
|
|
@ -56,7 +56,7 @@ class DbManager : Plugin() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadLocalMediaItem(localMediaItemId:String):LocalMediaItem? {
|
fun getLocalMediaItem(localMediaItemId:String):LocalMediaItem? {
|
||||||
return Paper.book("localMediaItems").read(localMediaItemId)
|
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
|
@PluginMethod
|
||||||
fun setCurrentServerConnectionConfig_WV(call:PluginCall) {
|
fun setCurrentServerConnectionConfig_WV(call:PluginCall) {
|
||||||
var serverConnectionConfigId = call.getString("id", "").toString()
|
var serverConnectionConfigId = call.getString("id", "").toString()
|
||||||
|
|
|
@ -18,6 +18,18 @@ data class DeviceData(
|
||||||
var lastServerConnectionConfigId:String?
|
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)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
data class LocalMediaItem(
|
data class LocalMediaItem(
|
||||||
var id:String,
|
var id:String,
|
||||||
|
@ -40,6 +52,13 @@ data class LocalMediaItem(
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
fun getTotalSize():Long {
|
||||||
|
var total = 0L
|
||||||
|
localFiles.forEach { total += it.size }
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getMediaMetadata():MediaTypeMetadata {
|
fun getMediaMetadata():MediaTypeMetadata {
|
||||||
return if (mediaType == "book") {
|
return if (mediaType == "book") {
|
||||||
|
@ -54,7 +73,36 @@ data class LocalMediaItem(
|
||||||
var sessionId = "play-${UUID.randomUUID()}"
|
var sessionId = "play-${UUID.randomUUID()}"
|
||||||
|
|
||||||
var mediaMetadata = getMediaMetadata()
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ class PlaybackSession(
|
||||||
var episodeId:String?,
|
var episodeId:String?,
|
||||||
var mediaType:String,
|
var mediaType:String,
|
||||||
var mediaMetadata:MediaTypeMetadata,
|
var mediaMetadata:MediaTypeMetadata,
|
||||||
var chapters:MutableList<BookChapter>,
|
var chapters:List<BookChapter>,
|
||||||
var displayTitle: String?,
|
var displayTitle: String?,
|
||||||
var displayAuthor: String?,
|
var displayAuthor: String?,
|
||||||
var coverPath:String?,
|
var coverPath:String?,
|
||||||
|
|
|
@ -11,9 +11,6 @@ import com.arthenica.ffmpegkit.Level
|
||||||
import com.audiobookshelf.app.data.*
|
import com.audiobookshelf.app.data.*
|
||||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.GlobalScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class FolderScanner(var ctx: Context) {
|
class FolderScanner(var ctx: Context) {
|
||||||
private val tag = "FolderScanner"
|
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}")
|
Log.d(tag, "Iterating over Folder Found ${it.name} | ${it.getSimplePath(ctx)} | URI: ${it.uri}")
|
||||||
|
|
||||||
var itemFolderName = it.name ?: ""
|
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 existingMediaItem = existingMediaItems.find { emi -> emi.id == itemId }
|
||||||
var existingLocalFiles = existingMediaItem?.localFiles ?: mutableListOf()
|
var existingLocalFiles = existingMediaItem?.localFiles ?: mutableListOf()
|
||||||
|
@ -130,7 +127,7 @@ class FolderScanner(var ctx: Context) {
|
||||||
audioTrackToAdd = existingAudioTrack
|
audioTrackToAdd = existingAudioTrack
|
||||||
} else {
|
} else {
|
||||||
// Create new audio track
|
// 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
|
audioTrackToAdd = track
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -175,15 +175,6 @@ export default {
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('TEST failed', 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() {
|
mounted() {
|
||||||
|
@ -195,7 +186,6 @@ export default {
|
||||||
|
|
||||||
this.setListeners()
|
this.setListeners()
|
||||||
this.$eventBus.$on('play-item', this.playLibraryItem)
|
this.$eventBus.$on('play-item', this.playLibraryItem)
|
||||||
this.$eventBus.$on('play-local-item', this.playLocalItem)
|
|
||||||
this.$eventBus.$on('close-stream', this.closeStreamOnly)
|
this.$eventBus.$on('close-stream', this.closeStreamOnly)
|
||||||
this.$store.commit('user/addSettingsListener', { id: 'streamContainer', meth: this.settingsUpdated })
|
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.$server.socket.off('stream_reset', this.streamReset)
|
||||||
// }
|
// }
|
||||||
this.$eventBus.$off('play-item', this.playLibraryItem)
|
this.$eventBus.$off('play-item', this.playLibraryItem)
|
||||||
this.$eventBus.$off('play-local-item', this.playLocalItem)
|
|
||||||
this.$eventBus.$off('close-stream', this.closeStreamOnly)
|
this.$eventBus.$off('close-stream', this.closeStreamOnly)
|
||||||
this.$store.commit('user/removeSettingsListener', 'streamContainer')
|
this.$store.commit('user/removeSettingsListener', 'streamContainer')
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 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 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>
|
<p :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }" class="font-book text-gray-300 text-center">{{ title }}</p>
|
||||||
</div>
|
</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 -->
|
<!-- 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' }">
|
<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>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { Capacitor } from '@capacitor/core'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
index: Number,
|
index: Number,
|
||||||
|
@ -78,7 +80,7 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isProcessingReadUpdate: false,
|
isProcessingReadUpdate: false,
|
||||||
audiobook: null,
|
libraryItem: null,
|
||||||
imageReady: false,
|
imageReady: false,
|
||||||
rescanning: false,
|
rescanning: false,
|
||||||
selected: false,
|
selected: false,
|
||||||
|
@ -90,7 +92,7 @@ export default {
|
||||||
bookMount: {
|
bookMount: {
|
||||||
handler(newVal) {
|
handler(newVal) {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
this.audiobook = newVal
|
this.libraryItem = newVal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,7 +102,11 @@ export default {
|
||||||
return this.store.state.showExperimentalFeatures
|
return this.store.state.showExperimentalFeatures
|
||||||
},
|
},
|
||||||
_libraryItem() {
|
_libraryItem() {
|
||||||
return this.audiobook || {}
|
return this.libraryItem || {}
|
||||||
|
},
|
||||||
|
isLocal() {
|
||||||
|
// Is local library item
|
||||||
|
return !!this._libraryItem.isLocal
|
||||||
},
|
},
|
||||||
media() {
|
media() {
|
||||||
return this._libraryItem.media || {}
|
return this._libraryItem.media || {}
|
||||||
|
@ -112,6 +118,10 @@ export default {
|
||||||
return '/book_placeholder.jpg'
|
return '/book_placeholder.jpg'
|
||||||
},
|
},
|
||||||
bookCoverSrc() {
|
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)
|
return this.store.getters['globals/getLibraryItemCoverSrc'](this._libraryItem, this.placeholderUrl)
|
||||||
},
|
},
|
||||||
libraryItemId() {
|
libraryItemId() {
|
||||||
|
@ -225,8 +235,8 @@ export default {
|
||||||
return this._libraryItem.hasInvalidParts
|
return this._libraryItem.hasInvalidParts
|
||||||
},
|
},
|
||||||
errorText() {
|
errorText() {
|
||||||
if (this.isMissing) return 'Audiobook directory is missing!'
|
if (this.isMissing) return 'Item directory is missing!'
|
||||||
else if (this.isInvalid) return 'Audiobook has no audio tracks & ebook'
|
else if (this.isInvalid) return 'Item has no media files'
|
||||||
var txt = ''
|
var txt = ''
|
||||||
if (this.hasMissingParts) {
|
if (this.hasMissingParts) {
|
||||||
txt = `${this.hasMissingParts} missing parts.`
|
txt = `${this.hasMissingParts} missing parts.`
|
||||||
|
@ -307,7 +317,7 @@ export default {
|
||||||
if (!val) this.selected = false
|
if (!val) this.selected = false
|
||||||
},
|
},
|
||||||
setEntity(libraryItem) {
|
setEntity(libraryItem) {
|
||||||
this.audiobook = libraryItem
|
this.libraryItem = libraryItem
|
||||||
},
|
},
|
||||||
clickCard(e) {
|
clickCard(e) {
|
||||||
if (this.isSelectionMode) {
|
if (this.isSelectionMode) {
|
||||||
|
@ -323,7 +333,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
editClick() {
|
editClick() {
|
||||||
this.$emit('edit', this.audiobook)
|
this.$emit('edit', this.libraryItem)
|
||||||
},
|
},
|
||||||
toggleFinished() {
|
toggleFinished() {
|
||||||
var updatePayload = {
|
var updatePayload = {
|
||||||
|
@ -369,27 +379,27 @@ export default {
|
||||||
},
|
},
|
||||||
showEditModalTracks() {
|
showEditModalTracks() {
|
||||||
// More menu func
|
// More menu func
|
||||||
this.store.commit('showEditModalOnTab', { libraryItem: this.audiobook, tab: 'tracks' })
|
this.store.commit('showEditModalOnTab', { libraryItem: this.libraryItem, tab: 'tracks' })
|
||||||
},
|
},
|
||||||
showEditModalMatch() {
|
showEditModalMatch() {
|
||||||
// More menu func
|
// More menu func
|
||||||
this.store.commit('showEditModalOnTab', { libraryItem: this.audiobook, tab: 'match' })
|
this.store.commit('showEditModalOnTab', { libraryItem: this.libraryItem, tab: 'match' })
|
||||||
},
|
},
|
||||||
showEditModalDownload() {
|
showEditModalDownload() {
|
||||||
// More menu func
|
// More menu func
|
||||||
this.store.commit('showEditModalOnTab', { libraryItem: this.audiobook, tab: 'download' })
|
this.store.commit('showEditModalOnTab', { libraryItem: this.libraryItem, tab: 'download' })
|
||||||
},
|
},
|
||||||
openCollections() {
|
openCollections() {
|
||||||
this.store.commit('setSelectedLibraryItem', this.audiobook)
|
this.store.commit('setSelectedLibraryItem', this.libraryItem)
|
||||||
this.store.commit('globals/setShowUserCollectionsModal', true)
|
this.store.commit('globals/setShowUserCollectionsModal', true)
|
||||||
},
|
},
|
||||||
clickReadEBook() {
|
clickReadEBook() {
|
||||||
this.store.commit('showEReader', this.audiobook)
|
this.store.commit('showEReader', this.libraryItem)
|
||||||
},
|
},
|
||||||
selectBtnClick() {
|
selectBtnClick() {
|
||||||
if (this.processingBatch) return
|
if (this.processingBatch) return
|
||||||
this.selected = !this.selected
|
this.selected = !this.selected
|
||||||
this.$emit('select', this.audiobook)
|
this.$emit('select', this.libraryItem)
|
||||||
},
|
},
|
||||||
play() {
|
play() {
|
||||||
var eventBus = this.$eventBus || this.$nuxt.$eventBus
|
var eventBus = this.$eventBus || this.$nuxt.$eventBus
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { Capacitor } from '@capacitor/core'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
libraryItem: {
|
libraryItem: {
|
||||||
|
@ -60,6 +62,10 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
isLocal() {
|
||||||
|
if (!this.libraryItem) return false
|
||||||
|
return this.libraryItem.isLocal
|
||||||
|
},
|
||||||
squareAspectRatio() {
|
squareAspectRatio() {
|
||||||
return this.bookCoverAspectRatio === 1
|
return this.bookCoverAspectRatio === 1
|
||||||
},
|
},
|
||||||
|
@ -98,6 +104,10 @@ export default {
|
||||||
return '/book_placeholder.jpg'
|
return '/book_placeholder.jpg'
|
||||||
},
|
},
|
||||||
fullCoverUrl() {
|
fullCoverUrl() {
|
||||||
|
if (this.isLocal) {
|
||||||
|
if (this.hasCover) return Capacitor.convertFileSrc(this.cover)
|
||||||
|
return this.placeholderUrl
|
||||||
|
}
|
||||||
if (this.downloadCover) return this.downloadCover
|
if (this.downloadCover) return this.downloadCover
|
||||||
if (!this.libraryItem) return null
|
if (!this.libraryItem) return null
|
||||||
var store = this.$store || this.$nuxt.$store
|
var store = this.$store || this.$nuxt.$store
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full h-full min-h-full relative">
|
<div class="w-full h-full min-h-full relative">
|
||||||
|
<div v-if="!loading" class="w-full">
|
||||||
<template v-for="(shelf, index) in shelves">
|
<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 }" />
|
<bookshelf-shelf :key="shelf.id" :label="shelf.label" :entities="shelf.entities" :type="shelf.type" :style="{ zIndex: shelves.length - index }" />
|
||||||
</template>
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="!shelves.length" class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
|
<div v-if="!shelves.length" class="absolute top-0 left-0 w-full h-full flex items-center justify-center">
|
||||||
<div>
|
<div>
|
||||||
|
@ -123,8 +125,48 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
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() {
|
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
|
var categories = await this.$axios
|
||||||
.$get(`/api/libraries/${this.currentLibraryId}/personalized?minified=1`)
|
.$get(`/api/libraries/${this.currentLibraryId}/personalized?minified=1`)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
|
@ -134,8 +176,9 @@ export default {
|
||||||
console.error('Failed to fetch categories', error)
|
console.error('Failed to fetch categories', error)
|
||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
this.shelves = categories
|
this.shelves = this.shelves.concat(categories)
|
||||||
console.log('Shelves', this.shelves)
|
}
|
||||||
|
this.loading = false
|
||||||
},
|
},
|
||||||
// async socketInit(isConnected) {
|
// async socketInit(isConnected) {
|
||||||
// if (isConnected && this.currentLibraryId) {
|
// if (isConnected && this.currentLibraryId) {
|
||||||
|
@ -150,15 +193,13 @@ export default {
|
||||||
async libraryChanged(libid) {
|
async libraryChanged(libid) {
|
||||||
if (this.isSocketConnected && this.currentLibraryId) {
|
if (this.isSocketConnected && this.currentLibraryId) {
|
||||||
await this.fetchCategories()
|
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) {
|
audiobookAdded(audiobook) {
|
||||||
console.log('Audiobook added', audiobook)
|
console.log('Audiobook added', audiobook)
|
||||||
// TODO: Check if audiobook would be on this shelf
|
// 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() {
|
initListeners() {
|
||||||
// this.$server.on('initialized', this.socketInit)
|
// this.$server.on('initialized', this.socketInit)
|
||||||
this.$eventBus.$on('library-changed', this.libraryChanged)
|
this.$eventBus.$on('library-changed', this.libraryChanged)
|
||||||
this.$eventBus.$on('downloads-loaded', this.downloadsLoaded)
|
// this.$eventBus.$on('downloads-loaded', this.downloadsLoaded)
|
||||||
},
|
},
|
||||||
removeListeners() {
|
removeListeners() {
|
||||||
// this.$server.off('initialized', this.socketInit)
|
// this.$server.off('initialized', this.socketInit)
|
||||||
this.$eventBus.$off('library-changed', this.libraryChanged)
|
this.$eventBus.$off('library-changed', this.libraryChanged)
|
||||||
this.$eventBus.$off('downloads-loaded', this.downloadsLoaded)
|
// this.$eventBus.$off('downloads-loaded', this.downloadsLoaded)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
|
@ -27,7 +27,21 @@
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<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 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>
|
<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>
|
<span v-if="!showPlay" class="px-2 text-base">Read {{ ebookFormat }}</span>
|
||||||
</ui-btn>
|
</ui-btn>
|
||||||
<ui-btn v-if="isConnected && showPlay && !isIos" color="primary" class="flex items-center justify-center" :padding-x="2" @click="downloadClick">
|
<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>
|
</ui-btn>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -61,16 +76,13 @@ export default {
|
||||||
var libraryItemId = params.id
|
var libraryItemId = params.id
|
||||||
var libraryItem = null
|
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) => {
|
libraryItem = await app.$axios.$get(`/api/items/${libraryItemId}?expanded=1`).catch((error) => {
|
||||||
console.error('Failed', error)
|
console.error('Failed', error)
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
var download = store.getters['downloads/getDownload'](libraryItemId)
|
|
||||||
if (download) {
|
|
||||||
libraryItem = download.libraryItem
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!libraryItem) {
|
if (!libraryItem) {
|
||||||
|
@ -91,6 +103,9 @@ export default {
|
||||||
isIos() {
|
isIos() {
|
||||||
return this.$platform === 'ios'
|
return this.$platform === 'ios'
|
||||||
},
|
},
|
||||||
|
isLocal() {
|
||||||
|
return this.libraryItem.isLocal
|
||||||
|
},
|
||||||
isConnected() {
|
isConnected() {
|
||||||
return this.$store.state.socketConnected
|
return this.$store.state.socketConnected
|
||||||
},
|
},
|
||||||
|
@ -182,18 +197,19 @@ export default {
|
||||||
if (!this.ebookFile) return null
|
if (!this.ebookFile) return null
|
||||||
return this.ebookFile.ebookFormat
|
return this.ebookFile.ebookFormat
|
||||||
},
|
},
|
||||||
isDownloadPreparing() {
|
// isDownloadPreparing() {
|
||||||
return this.downloadObj ? this.downloadObj.isPreparing : false
|
// return this.downloadObj ? this.downloadObj.isPreparing : false
|
||||||
},
|
// },
|
||||||
isDownloadPlayable() {
|
isDownloadPlayable() {
|
||||||
return this.downloadObj && !this.isDownloading && !this.isDownloadPreparing
|
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)
|
|
||||||
},
|
},
|
||||||
|
// downloadedCover() {
|
||||||
|
// return this.downloadObj ? this.downloadObj.cover : null
|
||||||
|
// },
|
||||||
|
// downloadObj() {
|
||||||
|
// return this.$store.getters['downloads/getDownload'](this.libraryItemId)
|
||||||
|
// },
|
||||||
hasStoragePermission() {
|
hasStoragePermission() {
|
||||||
return this.$store.state.hasStoragePermission
|
return this.$store.state.hasStoragePermission
|
||||||
}
|
}
|
||||||
|
@ -264,7 +280,7 @@ export default {
|
||||||
this.download()
|
this.download()
|
||||||
},
|
},
|
||||||
async download(selectedLocalFolder = null) {
|
async download(selectedLocalFolder = null) {
|
||||||
if (!this.numTracks || this.downloadObj) {
|
if (!this.numTracks) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
play(mediaItem) {
|
play(mediaItem) {
|
||||||
this.$eventBus.$emit('play-local-item', mediaItem.id)
|
this.$eventBus.$emit('play-item', mediaItem.id)
|
||||||
},
|
},
|
||||||
async scanFolder(forceAudioProbe = false) {
|
async scanFolder(forceAudioProbe = false) {
|
||||||
this.isScanning = true
|
this.isScanning = true
|
||||||
|
|
|
@ -87,6 +87,22 @@ class DbService {
|
||||||
return data.localMediaItems
|
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) => {
|
export default ({ app, store }, inject) => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue