Update folder scanner and db to store LocalLibraryItem objects instead of LocalMediaItem objects, some ui fixes and audio player service binding fix

This commit is contained in:
advplyr 2022-04-05 19:44:14 -05:00
parent 77ef0c119b
commit 12de187b7a
22 changed files with 248 additions and 158 deletions

View file

@ -71,7 +71,7 @@
<service <service
android:exported="true" android:exported="true"
android:enabled="true" android:enabled="true"
android:name=".PlayerNotificationService"> android:name=".player.PlayerNotificationService">
<intent-filter> <intent-filter>
<action android:name="android.media.browse.MediaBrowserService"/> <action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter> </intent-filter>

View file

@ -10,7 +10,6 @@ import androidx.core.app.ActivityCompat
import com.anggrayudi.storage.SimpleStorage import com.anggrayudi.storage.SimpleStorage
import com.anggrayudi.storage.SimpleStorageHelper import com.anggrayudi.storage.SimpleStorageHelper
import com.audiobookshelf.app.data.AbsDatabase import com.audiobookshelf.app.data.AbsDatabase
import com.audiobookshelf.app.data.DbManager
import com.audiobookshelf.app.player.PlayerNotificationService import com.audiobookshelf.app.player.PlayerNotificationService
import com.audiobookshelf.app.plugins.AbsDownloader import com.audiobookshelf.app.plugins.AbsDownloader
import com.audiobookshelf.app.plugins.AbsAudioPlayer import com.audiobookshelf.app.plugins.AbsAudioPlayer
@ -18,7 +17,6 @@ import com.audiobookshelf.app.plugins.AbsFileSystem
import com.getcapacitor.BridgeActivity import com.getcapacitor.BridgeActivity
import io.paperdb.Paper import io.paperdb.Paper
class MainActivity : BridgeActivity() { class MainActivity : BridgeActivity() {
private val tag = "MainActivity" private val tag = "MainActivity"
@ -87,6 +85,7 @@ class MainActivity : BridgeActivity() {
override fun onPostCreate(savedInstanceState: Bundle?) { override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState) super.onPostCreate(savedInstanceState)
Log.d(tag, "onPostCreate MainActivity")
mConnection = object : ServiceConnection { mConnection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName) { override fun onServiceDisconnected(name: ComponentName) {
@ -97,7 +96,6 @@ class MainActivity : BridgeActivity() {
override fun onServiceConnected(name: ComponentName, service: IBinder) { override fun onServiceConnected(name: ComponentName, service: IBinder) {
Log.d(tag, "Service Connected $name") Log.d(tag, "Service Connected $name")
mBounded = true mBounded = true
val mLocalBinder = service as PlayerNotificationService.LocalBinder val mLocalBinder = service as PlayerNotificationService.LocalBinder
foregroundService = mLocalBinder.getService() foregroundService = mLocalBinder.getService()
@ -109,8 +107,10 @@ class MainActivity : BridgeActivity() {
} }
} }
val startIntent = Intent(this, PlayerNotificationService::class.java) Intent(this, PlayerNotificationService::class.java).also { intent ->
bindService(startIntent, mConnection as ServiceConnection, Context.BIND_AUTO_CREATE); Log.d(tag, "Binding PlayerNotificationService")
bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
}
} }

View file

@ -30,28 +30,68 @@ data class LibraryItem(
JsonSubTypes.Type(Book::class), JsonSubTypes.Type(Book::class),
JsonSubTypes.Type(Podcast::class) JsonSubTypes.Type(Podcast::class)
) )
open class MediaType {} open class MediaType(var metadata:MediaTypeMetadata, var coverPath:String?) {
@JsonIgnore
open fun getAudioTracks():List<AudioTrack> { return mutableListOf() }
@JsonIgnore
open fun setAudioTracks(audioTracks:List<AudioTrack>) { }
}
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class Podcast( class Podcast(
var metadata:PodcastMetadata, metadata:PodcastMetadata,
var coverPath:String?, coverPath:String?,
var tags:MutableList<String>, var tags:MutableList<String>,
var episodes:MutableList<PodcastEpisode>, var episodes:MutableList<PodcastEpisode>,
var autoDownloadEpisodes:Boolean var autoDownloadEpisodes:Boolean
) : MediaType() ) : MediaType(metadata, coverPath) {
@JsonIgnore
override fun getAudioTracks():List<AudioTrack> {
var tracks = episodes.map { it.audioTrack }
return tracks.filterNotNull()
}
@JsonIgnore
override fun setAudioTracks(audioTracks:List<AudioTrack>) {
// Remove episodes no longer there in tracks
episodes = episodes.filter { ep ->
audioTracks.find { it.localFileId == ep.audioTrack?.localFileId } != null
} as MutableList<PodcastEpisode>
// Add new episodes
audioTracks.forEach { at ->
if (episodes.find{ it.audioTrack?.localFileId == at.localFileId } == null) {
var newEpisode = PodcastEpisode("local_" + at.localFileId,episodes.size + 1,null,null,at.title,null,null,null,at)
episodes.add(newEpisode)
}
}
}
}
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class Book( class Book(
var metadata:BookMetadata, metadata:BookMetadata,
var coverPath:String?, coverPath:String?,
var tags:List<String>, var tags:List<String>,
var audioFiles:List<AudioFile>, var audioFiles:List<AudioFile>,
var chapters:List<BookChapter>, var chapters:List<BookChapter>,
var tracks:List<AudioTrack>?, var tracks:List<AudioTrack>?,
var size:Long?, var size:Long?,
var duration:Double? var duration:Double?
) : MediaType() ) : MediaType(metadata, coverPath) {
@JsonIgnore
override fun getAudioTracks():List<AudioTrack> {
return tracks ?: mutableListOf()
}
@JsonIgnore
override fun setAudioTracks(audioTracks:List<AudioTrack>) {
tracks = audioTracks
var totalDuration = 0.0
tracks?.forEach {
totalDuration += it.duration
}
duration = totalDuration
}
}
// This auto-detects whether it is a Book or Podcast // This auto-detects whether it is a Book or Podcast
@JsonTypeInfo(use=JsonTypeInfo.Id.DEDUCTION) @JsonTypeInfo(use=JsonTypeInfo.Id.DEDUCTION)
@ -59,11 +99,11 @@ data class Book(
JsonSubTypes.Type(BookMetadata::class), JsonSubTypes.Type(BookMetadata::class),
JsonSubTypes.Type(PodcastMetadata::class) JsonSubTypes.Type(PodcastMetadata::class)
) )
open class MediaTypeMetadata {} open class MediaTypeMetadata(var title:String) {}
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class BookMetadata( class BookMetadata(
var title:String, title:String,
var subtitle:String?, var subtitle:String?,
var authors:MutableList<Author>, var authors:MutableList<Author>,
var narrators:MutableList<String>, var narrators:MutableList<String>,
@ -81,15 +121,15 @@ data class BookMetadata(
var authorNameLF:String?, var authorNameLF:String?,
var narratorName:String?, var narratorName:String?,
var seriesName:String? var seriesName:String?
) : MediaTypeMetadata() ) : MediaTypeMetadata(title)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class PodcastMetadata( class PodcastMetadata(
var title:String, title:String,
var author:String?, var author:String?,
var feedUrl:String?, var feedUrl:String?,
var genres:MutableList<String> var genres:MutableList<String>
) : MediaTypeMetadata() ) : MediaTypeMetadata(title)
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class Author( data class Author(
@ -107,7 +147,8 @@ data class PodcastEpisode(
var title:String?, var title:String?,
var subtitle:String?, var subtitle:String?,
var description:String?, var description:String?,
var audioFile:AudioFile var audioFile:AudioFile?,
var audioTrack:AudioTrack?
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)

View file

@ -14,11 +14,11 @@ class DbManager {
Paper.book("device").write("data", deviceData) Paper.book("device").write("data", deviceData)
} }
fun getLocalMediaItems():MutableList<LocalMediaItem> { fun getLocalLibraryItems():MutableList<LocalLibraryItem> {
var localMediaItems:MutableList<LocalMediaItem> = mutableListOf() var localLibraryItems:MutableList<LocalLibraryItem> = mutableListOf()
Paper.book("localMediaItems").allKeys.forEach { Paper.book("localLibraryItems").allKeys.forEach {
var localMediaItem:LocalMediaItem? = Paper.book("localMediaItems").read(it) var localLibraryItem:LocalLibraryItem? = Paper.book("localLibraryItems").read(it)
if (localMediaItem != null) { if (localLibraryItem != null) {
// TODO: Check to make sure all file paths exist // TODO: Check to make sure all file paths exist
// if (localMediaItem.coverContentUrl != null) { // if (localMediaItem.coverContentUrl != null) {
// var file = DocumentFile.fromSingleUri(ctx) // var file = DocumentFile.fromSingleUri(ctx)
@ -29,31 +29,31 @@ class DbManager {
// localMediaItems.add(localMediaItem) // localMediaItems.add(localMediaItem)
// } // }
// } else { // } else {
localMediaItems.add(localMediaItem) localLibraryItems.add(localLibraryItem)
// } // }
} }
} }
return localMediaItems return localLibraryItems
} }
fun getLocalMediaItemsInFolder(folderId:String):List<LocalMediaItem> { fun getLocalLibraryItemsInFolder(folderId:String):List<LocalLibraryItem> {
var localMediaItems = getLocalMediaItems() var localLibraryItems = getLocalLibraryItems()
return localMediaItems.filter { return localLibraryItems.filter {
it.folderId == folderId it.folderId == folderId
} }
} }
fun getLocalMediaItem(localMediaItemId:String):LocalMediaItem? { fun getLocalLibraryItem(localLibraryItemId:String):LocalLibraryItem? {
return Paper.book("localMediaItems").read(localMediaItemId) return Paper.book("localLibraryItems").read(localLibraryItemId)
} }
fun removeLocalMediaItem(localMediaItemId:String) { fun removeLocalLibraryItem(localLibraryItemId:String) {
Paper.book("localMediaItems").delete(localMediaItemId) Paper.book("localLibraryItems").delete(localLibraryItemId)
} }
fun saveLocalMediaItems(localMediaItems:List<LocalMediaItem>) { fun saveLocalLibraryItems(localLibraryItems:List<LocalLibraryItem>) {
localMediaItems.map { localLibraryItems.map {
Paper.book("localMediaItems").write(it.id, it) Paper.book("localLibraryItems").write(it.id, it)
} }
} }
@ -77,9 +77,9 @@ class DbManager {
} }
fun removeLocalFolder(folderId:String) { fun removeLocalFolder(folderId:String) {
var localMediaItems = getLocalMediaItemsInFolder(folderId) var localLibraryItems = getLocalLibraryItemsInFolder(folderId)
localMediaItems.forEach { localLibraryItems.forEach {
Paper.book("localMediaItems").delete(it.id) Paper.book("localLibraryItems").delete(it.id)
} }
Paper.book("localFolders").delete(folderId) Paper.book("localFolders").delete(folderId)
} }

View file

@ -22,14 +22,54 @@ data class DeviceData(
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class LocalLibraryItem( data class LocalLibraryItem(
var id:String, var id:String,
var libraryItemId:String?,
var folderId:String, var folderId:String,
var absolutePath:String, var absolutePath:String,
var isInvalid:Boolean, var isInvalid:Boolean,
var mediaType:String, var mediaType:String,
var media:MediaType, var media:MediaType,
var localFiles:MutableList<LocalFile>, var localFiles:MutableList<LocalFile>,
var coverContentUrl:String?,
var coverAbsolutePath:String?,
var isLocal:Boolean var isLocal:Boolean
) ) {
@JsonIgnore
fun getDuration():Double {
var total = 0.0
var audioTracks = media.getAudioTracks()
audioTracks.forEach{ total += it.duration }
return total
}
@JsonIgnore
fun updateFromScan(audioTracks:List<AudioTrack>, _localFiles:MutableList<LocalFile>) {
media.setAudioTracks(audioTracks)
localFiles = _localFiles
if (coverContentUrl != null) {
if (localFiles.find { it.contentUrl == coverContentUrl } == null) {
// Cover was removed
coverContentUrl = null
coverAbsolutePath = null
media.coverPath = null
}
}
}
@JsonIgnore
fun getPlaybackSession():PlaybackSession {
var sessionId = "play-${UUID.randomUUID()}"
var mediaMetadata = media.metadata
var chapters = if (mediaType == "book") (media as Book).chapters else mutableListOf()
var authorName = "Unknown"
if (mediaType == "book") {
var bookMetadata = mediaMetadata as BookMetadata
authorName = bookMetadata?.authorName ?: "Unknown"
}
return PlaybackSession(sessionId,null,null,null, mediaType, mediaMetadata, chapters, mediaMetadata.title, authorName,null,getDuration(),PLAYMETHOD_LOCAL, media.getAudioTracks() as MutableList<AudioTrack>,0.0,null,this,null,null)
}
}
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class LocalMediaItem( data class LocalMediaItem(
@ -69,20 +109,6 @@ data class LocalMediaItem(
} }
} }
@JsonIgnore
fun getPlaybackSession():PlaybackSession {
var sessionId = "play-${UUID.randomUUID()}"
var mediaMetadata = getMediaMetadata()
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 @JsonIgnore
fun getAudiobookChapters():List<BookChapter> { fun getAudiobookChapters():List<BookChapter> {
if (mediaType != "book" || audioTracks.isEmpty()) return mutableListOf() if (mediaType != "book" || audioTracks.isEmpty()) return mutableListOf()
@ -98,11 +124,11 @@ data class LocalMediaItem(
var mediaMetadata = getMediaMetadata() var mediaMetadata = getMediaMetadata()
if (mediaType == "book") { if (mediaType == "book") {
var chapters = getAudiobookChapters() var chapters = getAudiobookChapters()
var book = Book(mediaMetadata as BookMetadata, coverContentUrl, mutableListOf(), mutableListOf(), chapters,audioTracks,getTotalSize(),getDuration()) var book = Book(mediaMetadata as BookMetadata, coverAbsolutePath, mutableListOf(), mutableListOf(), chapters,audioTracks,getTotalSize(),getDuration())
return LocalLibraryItem(id, folderId, absolutePath, false,mediaType, book, localFiles, true) return LocalLibraryItem(id, null, folderId, absolutePath, false,mediaType, book, localFiles, coverContentUrl, coverAbsolutePath,true)
} else { } else {
var podcast = Podcast(mediaMetadata as PodcastMetadata, coverContentUrl, mutableListOf(), mutableListOf(), false) var podcast = Podcast(mediaMetadata as PodcastMetadata, coverAbsolutePath, mutableListOf(), mutableListOf(), false)
return LocalLibraryItem(id, folderId, absolutePath, false, mediaType, podcast,localFiles,true) return LocalLibraryItem(id, null, folderId, absolutePath, false, mediaType, podcast,localFiles,coverContentUrl, coverAbsolutePath, true)
} }
} }
} }

View file

@ -6,5 +6,5 @@ data class FolderScanResult(
var itemsRemoved:Int, var itemsRemoved:Int,
var itemsUpToDate:Int, var itemsUpToDate:Int,
val localFolder:LocalFolder, val localFolder:LocalFolder,
val localMediaItems:List<LocalMediaItem>, val localLibraryItems:List<LocalLibraryItem>,
) )

View file

@ -32,7 +32,7 @@ class PlaybackSession(
var audioTracks:MutableList<AudioTrack>, var audioTracks:MutableList<AudioTrack>,
var currentTime:Double, var currentTime:Double,
var libraryItem:LibraryItem?, var libraryItem:LibraryItem?,
var localMediaItem:LocalMediaItem?, var localLibraryItem:LocalLibraryItem?,
var serverUrl:String?, var serverUrl:String?,
var token:String? var token:String?
) { ) {
@ -74,7 +74,7 @@ class PlaybackSession(
@JsonIgnore @JsonIgnore
fun getCoverUri(): Uri { fun getCoverUri(): Uri {
if (localMediaItem?.coverContentUrl != null) return Uri.parse(localMediaItem?.coverContentUrl) ?: Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon) if (localLibraryItem?.coverContentUrl != null) return Uri.parse(localLibraryItem?.coverContentUrl) ?: Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon)
if (coverPath == null) return Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon) if (coverPath == null) return Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon)
return Uri.parse("$serverUrl/api/items/$libraryItemId/cover?token=$token") return Uri.parse("$serverUrl/api/items/$libraryItemId/cover?token=$token")

View file

@ -15,6 +15,10 @@ import com.fasterxml.jackson.module.kotlin.readValue
class FolderScanner(var ctx: Context) { class FolderScanner(var ctx: Context) {
private val tag = "FolderScanner" private val tag = "FolderScanner"
private fun getLocalLibraryItemId(mediaItemId:String):String {
return "local_" + DeviceManager.getBase64Id(mediaItemId)
}
// TODO: CLEAN this monster! Divide into bite-size methods // TODO: CLEAN this monster! Divide into bite-size methods
fun scanForMediaItems(localFolder:LocalFolder, forceAudioProbe:Boolean):FolderScanResult? { fun scanForMediaItems(localFolder:LocalFolder, forceAudioProbe:Boolean):FolderScanResult? {
FFmpegKitConfig.enableLogCallback { log -> FFmpegKitConfig.enableLogCallback { log ->
@ -38,32 +42,32 @@ class FolderScanner(var ctx: Context) {
// Search for files in media item folder // Search for files in media item folder
var foldersFound = df.search(false, DocumentFileType.FOLDER) var foldersFound = df.search(false, DocumentFileType.FOLDER)
// Match folders found with media items already saved in db // Match folders found with local library items already saved in db
var existingMediaItems = DeviceManager.dbManager.getLocalMediaItemsInFolder(localFolder.id) var existingLocalLibraryItems = DeviceManager.dbManager.getLocalLibraryItemsInFolder(localFolder.id)
// Remove existing items no longer there // Remove existing items no longer there
existingMediaItems = existingMediaItems.filter { lmi -> existingLocalLibraryItems = existingLocalLibraryItems.filter { lli ->
var fileFound = foldersFound.find { f -> lmi.id == DeviceManager.getBase64Id(f.id) } var fileFound = foldersFound.find { f -> lli.id == getLocalLibraryItemId(f.id) }
if (fileFound == null) { if (fileFound == null) {
Log.d(tag, "Existing media item is no longer in file system ${lmi.name}") Log.d(tag, "Existing local library item is no longer in file system ${lli.media.metadata.title}")
DeviceManager.dbManager.removeLocalMediaItem(lmi.id) DeviceManager.dbManager.removeLocalLibraryItem(lli.id)
mediaItemsRemoved++ mediaItemsRemoved++
} }
fileFound != null fileFound != null
} }
var mediaItems = mutableListOf<LocalMediaItem>() var localLibraryItems = mutableListOf<LocalLibraryItem>()
foldersFound.forEach { foldersFound.forEach { itemFolder ->
Log.d(tag, "Iterating over Folder Found ${it.name} | ${it.getSimplePath(ctx)} | URI: ${it.uri}") Log.d(tag, "Iterating over Folder Found ${itemFolder.name} | ${itemFolder.getSimplePath(ctx)} | URI: ${itemFolder.uri}")
var itemFolderName = it.name ?: "" var itemFolderName = itemFolder.name ?: ""
var itemId = "local_" + DeviceManager.getBase64Id(it.id) var itemId = getLocalLibraryItemId(itemFolder.id)
var existingMediaItem = existingMediaItems.find { emi -> emi.id == itemId } var existingItem = existingLocalLibraryItems.find { emi -> emi.id == itemId }
var existingLocalFiles = existingMediaItem?.localFiles ?: mutableListOf() var existingLocalFiles = existingItem?.localFiles ?: mutableListOf()
var existingAudioTracks = existingMediaItem?.audioTracks ?: mutableListOf() var existingAudioTracks = existingItem?.media?.getAudioTracks() ?: mutableListOf()
var isNewOrUpdated = existingMediaItem == null var isNewOrUpdated = existingItem == null
var audioTracks = mutableListOf<AudioTrack>() var audioTracks = mutableListOf<AudioTrack>()
var localFiles = mutableListOf<LocalFile>() var localFiles = mutableListOf<LocalFile>()
@ -72,13 +76,14 @@ class FolderScanner(var ctx: Context) {
var coverContentUrl:String? = null var coverContentUrl:String? = null
var coverAbsolutePath:String? = null var coverAbsolutePath:String? = null
var filesInFolder = it.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*")) var filesInFolder = itemFolder.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*"))
var existingLocalFilesRemoved = existingLocalFiles.filter { elf -> var existingLocalFilesRemoved = existingLocalFiles.filter { elf ->
filesInFolder.find { fif -> DeviceManager.getBase64Id(fif.id) == elf.id } == null // File was not found in media item folder filesInFolder.find { fif -> DeviceManager.getBase64Id(fif.id) == elf.id } == null // File was not found in media item folder
} }
if (existingLocalFilesRemoved.isNotEmpty()) { if (existingLocalFilesRemoved.isNotEmpty()) {
Log.d(tag, "${existingLocalFilesRemoved.size} Local files were removed from local media item ${existingMediaItem?.name}") Log.d(tag, "${existingLocalFilesRemoved.size} Local files were removed from local media item ${existingItem?.media?.metadata?.title}")
isNewOrUpdated = true isNewOrUpdated = true
} }
@ -147,9 +152,12 @@ class FolderScanner(var ctx: Context) {
if (existingLocalFile == null) { if (existingLocalFile == null) {
isNewOrUpdated = true isNewOrUpdated = true
} }
if (existingMediaItem != null && existingMediaItem.coverContentUrl == null) { if (existingItem != null && existingItem.coverContentUrl == null) {
// Existing media item did not have a cover - cover found on scan // Existing media item did not have a cover - cover found on scan
isNewOrUpdated = true isNewOrUpdated = true
existingItem.coverAbsolutePath = localFile.absolutePath
existingItem.coverContentUrl = localFile.contentUrl
existingItem.media.coverPath = localFile.absolutePath
} }
// First image file use as cover path // First image file use as cover path
@ -160,30 +168,36 @@ class FolderScanner(var ctx: Context) {
} }
} }
if (existingMediaItem != null && audioTracks.isEmpty()) { if (existingItem != null && audioTracks.isEmpty()) {
Log.d(tag, "Local media item ${existingMediaItem.name} no longer has audio tracks - removing item") Log.d(tag, "Local library item ${existingItem.media.metadata.title} no longer has audio tracks - removing item")
DeviceManager.dbManager.removeLocalMediaItem(existingMediaItem.id) DeviceManager.dbManager.removeLocalLibraryItem(existingItem.id)
mediaItemsRemoved++ mediaItemsRemoved++
} else if (existingMediaItem != null && !isNewOrUpdated) { } else if (existingItem != null && !isNewOrUpdated) {
Log.d(tag, "Local media item ${existingMediaItem.name} has no updates") Log.d(tag, "Local library item ${existingItem.media.metadata.title} has no updates")
mediaItemsUpToDate++ mediaItemsUpToDate++
} else if (audioTracks.isNotEmpty()) { } else if (existingItem != null) {
if (existingMediaItem != null) mediaItemsUpdated++ Log.d(tag, "Updating local library item ${existingItem.media.metadata.title}")
else mediaItemsAdded++ mediaItemsUpdated++
Log.d(tag, "Found local media item named $itemFolderName with ${audioTracks.size} tracks and ${localFiles.size} local files") existingItem.updateFromScan(audioTracks,localFiles)
var localMediaItem = LocalMediaItem(itemId, itemFolderName, localFolder.mediaType, localFolder.id, it.uri.toString(), it.getSimplePath(ctx), it.getAbsolutePath(ctx),audioTracks,localFiles,coverContentUrl,coverAbsolutePath) localLibraryItems.add(existingItem)
mediaItems.add(localMediaItem) } else if (audioTracks.isNotEmpty()) {
Log.d(tag, "Found local media item named $itemFolderName with ${audioTracks.size} tracks and ${localFiles.size} local files")
mediaItemsAdded++
var localMediaItem = LocalMediaItem(itemId, itemFolderName, localFolder.mediaType, localFolder.id, itemFolder.uri.toString(), itemFolder.getSimplePath(ctx), itemFolder.getAbsolutePath(ctx),audioTracks,localFiles,coverContentUrl,coverAbsolutePath)
var localLibraryItem = localMediaItem.getLocalLibraryItem()
localLibraryItems.add(localLibraryItem)
} }
} }
Log.d(tag, "Folder $${localFolder.name} scan Results: $mediaItemsAdded Added | $mediaItemsUpdated Updated | $mediaItemsRemoved Removed | $mediaItemsUpToDate Up-to-date") Log.d(tag, "Folder $${localFolder.name} scan Results: $mediaItemsAdded Added | $mediaItemsUpdated Updated | $mediaItemsRemoved Removed | $mediaItemsUpToDate Up-to-date")
return if (mediaItems.isNotEmpty()) { return if (localLibraryItems.isNotEmpty()) {
DeviceManager.dbManager.saveLocalMediaItems(mediaItems) DeviceManager.dbManager.saveLocalLibraryItems(localLibraryItems)
var folderMediaItems = DeviceManager.dbManager.getLocalMediaItemsInFolder(localFolder.id) // Get all local media items var folderLibraryItems = DeviceManager.dbManager.getLocalLibraryItemsInFolder(localFolder.id) // Get all local media items
FolderScanResult(mediaItemsAdded, mediaItemsUpdated, mediaItemsRemoved, mediaItemsUpToDate, localFolder, folderMediaItems) FolderScanResult(mediaItemsAdded, mediaItemsUpdated, mediaItemsRemoved, mediaItemsUpToDate, localFolder, folderLibraryItems)
} else { } else {
Log.d(tag, "No Media Items to save") Log.d(tag, "No Media Items to save")
FolderScanResult(mediaItemsAdded, mediaItemsUpdated, mediaItemsRemoved, mediaItemsUpToDate, localFolder, mutableListOf()) FolderScanResult(mediaItemsAdded, mediaItemsUpdated, mediaItemsRemoved, mediaItemsUpToDate, localFolder, mutableListOf())

View file

@ -133,8 +133,8 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d(tag, "onStartCommand $startId")
isStarted = true isStarted = true
Log.d(tag, "onStartCommand $startId")
return START_STICKY return START_STICKY
} }

View file

@ -1,8 +1,10 @@
package com.audiobookshelf.app.plugins package com.audiobookshelf.app.plugins
import android.content.Intent
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
import androidx.core.content.ContextCompat
import com.audiobookshelf.app.MainActivity import com.audiobookshelf.app.MainActivity
import com.audiobookshelf.app.data.PlaybackSession import com.audiobookshelf.app.data.PlaybackSession
import com.audiobookshelf.app.device.DeviceManager import com.audiobookshelf.app.device.DeviceManager
@ -75,11 +77,19 @@ class AbsAudioPlayer : Plugin() {
@PluginMethod @PluginMethod
fun prepareLibraryItem(call: PluginCall) { fun prepareLibraryItem(call: PluginCall) {
// Need to make sure the player service has been started
if (!PlayerNotificationService.isStarted) {
Log.w(tag, "prepareLibraryItem: PlayerService not started - Starting foreground service --")
Intent(mainActivity, PlayerNotificationService::class.java).also { intent ->
ContextCompat.startForegroundService(mainActivity, intent)
}
}
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 if (libraryItemId.startsWith("local")) { // Play local media item
DeviceManager.dbManager.getLocalMediaItem(libraryItemId)?.let { DeviceManager.dbManager.getLocalLibraryItem(libraryItemId)?.let {
Handler(Looper.getMainLooper()).post() { Handler(Looper.getMainLooper()).post() {
Log.d(tag, "Preparing Local Media item ${jacksonObjectMapper().writeValueAsString(it)}") Log.d(tag, "Preparing Local Media item ${jacksonObjectMapper().writeValueAsString(it)}")
var playbackSession = it.getPlaybackSession() var playbackSession = it.getPlaybackSession()

View file

@ -48,23 +48,22 @@ class AbsDatabase : Plugin() {
} }
@PluginMethod @PluginMethod
fun getLocalMediaItemsInFolder(call:PluginCall) { fun getLocalLibraryItem(call:PluginCall) {
var folderId = call.getString("folderId", "").toString() var id = call.getString("id", "").toString()
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
var localMediaItems = DeviceManager.dbManager.getLocalMediaItemsInFolder(folderId) var localLibraryItem = DeviceManager.dbManager.getLocalLibraryItem(id)
var mediaItemsArray = jacksonObjectMapper().writeValueAsString(localMediaItems) if (localLibraryItem == null) {
var jsobj = JSObject() call.resolve()
jsobj.put("localMediaItems", mediaItemsArray) } else {
call.resolve(jsobj) call.resolve(JSObject(jacksonObjectMapper().writeValueAsString(localLibraryItem)))
}
} }
} }
@PluginMethod @PluginMethod
fun getLocalLibraryItems(call:PluginCall) { fun getLocalLibraryItems(call:PluginCall) {
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
var localLibraryItems = DeviceManager.dbManager.getLocalMediaItems().map { var localLibraryItems = DeviceManager.dbManager.getLocalLibraryItems()
it.getLocalLibraryItem()
}
var jsobj = JSObject() var jsobj = JSObject()
jsobj.put("localLibraryItems", jacksonObjectMapper().writeValueAsString(localLibraryItems)) jsobj.put("localLibraryItems", jacksonObjectMapper().writeValueAsString(localLibraryItems))
call.resolve(jsobj) call.resolve(jsobj)
@ -72,16 +71,14 @@ class AbsDatabase : Plugin() {
} }
@PluginMethod @PluginMethod
fun getLocalLibraryItem(call:PluginCall) { fun getLocalLibraryItemsInFolder(call:PluginCall) {
var id = call.getString("id", "").toString() var folderId = call.getString("folderId", "").toString()
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
var mediaItem = DeviceManager.dbManager.getLocalMediaItem(id) var localMediaItems = DeviceManager.dbManager.getLocalLibraryItemsInFolder(folderId)
var localLibraryItem = mediaItem?.getLocalLibraryItem() var mediaItemsArray = jacksonObjectMapper().writeValueAsString(localMediaItems)
if (localLibraryItem == null) { var jsobj = JSObject()
call.resolve() jsobj.put("localLibraryItems", mediaItemsArray)
} else { call.resolve(jsobj)
call.resolve(JSObject(jacksonObjectMapper().writeValueAsString(localLibraryItem)))
}
} }
} }

View file

@ -118,11 +118,11 @@ class AbsDownloader : Plugin() {
fun getAbMetadataText(libraryItem:LibraryItem):String { fun getAbMetadataText(libraryItem:LibraryItem):String {
var bookMedia = libraryItem.media as com.audiobookshelf.app.data.Book var bookMedia = libraryItem.media as com.audiobookshelf.app.data.Book
var fileString = ";ABMETADATA1\n" var fileString = ";ABMETADATA1\n"
fileString += "#libraryItemId=${libraryItem.id}\n" // fileString += "#libraryItemId=${libraryItem.id}\n"
fileString += "title=${bookMedia.metadata.title}\n" // fileString += "title=${bookMedia.metadata.title}\n"
fileString += "author=${bookMedia.metadata.authorName}\n" // fileString += "author=${bookMedia.metadata.authorName}\n"
fileString += "narrator=${bookMedia.metadata.narratorName}\n" // fileString += "narrator=${bookMedia.metadata.narratorName}\n"
fileString += "series=${bookMedia.metadata.seriesName}\n" // fileString += "series=${bookMedia.metadata.seriesName}\n"
return fileString return fileString
} }

View file

@ -1,5 +1,5 @@
<template> <template>
<div v-if="playbackSession" class="fixed top-0 left-0 layout-wrapper right-0 z-50 pointer-events-none" :class="showFullscreen ? 'fullscreen' : ''"> <div v-if="playbackSession" id="streamContainer" class="fixed top-0 left-0 layout-wrapper right-0 z-50 pointer-events-none" :class="showFullscreen ? 'fullscreen' : ''">
<div v-if="showFullscreen" class="w-full h-full z-10 bg-bg absolute top-0 left-0 pointer-events-auto"> <div v-if="showFullscreen" class="w-full h-full z-10 bg-bg absolute top-0 left-0 pointer-events-auto">
<div class="top-2 left-4 absolute cursor-pointer"> <div class="top-2 left-4 absolute cursor-pointer">
<span class="material-icons text-5xl" @click="collapseFullscreen">expand_more</span> <span class="material-icons text-5xl" @click="collapseFullscreen">expand_more</span>
@ -31,7 +31,7 @@
<div class="cover-wrapper absolute z-30 pointer-events-auto" :class="bookCoverAspectRatio === 1 ? 'square-cover' : ''" @click="clickContainer"> <div class="cover-wrapper absolute z-30 pointer-events-auto" :class="bookCoverAspectRatio === 1 ? 'square-cover' : ''" @click="clickContainer">
<div class="cover-container bookCoverWrapper bg-black bg-opacity-75 w-full h-full"> <div class="cover-container bookCoverWrapper bg-black bg-opacity-75 w-full h-full">
<covers-book-cover v-if="libraryItem || localMediaItemCoverSrc" :library-item="libraryItem" :download-cover="localMediaItemCoverSrc" :width="bookCoverWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" /> <covers-book-cover v-if="libraryItem || localLibraryItemCoverSrc" :library-item="libraryItem" :download-cover="localLibraryItemCoverSrc" :width="bookCoverWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" />
</div> </div>
</div> </div>
@ -175,12 +175,12 @@ export default {
libraryItem() { libraryItem() {
return this.playbackSession ? this.playbackSession.libraryItem || null : null return this.playbackSession ? this.playbackSession.libraryItem || null : null
}, },
localMediaItem() { localLibraryItem() {
return this.playbackSession ? this.playbackSession.localMediaItem || null : null return this.playbackSession ? this.playbackSession.localLibraryItem || null : null
}, },
localMediaItemCoverSrc() { localLibraryItemCoverSrc() {
var localMediaItemCover = this.localMediaItem ? this.localMediaItem.coverContentUrl : null var localItemCover = this.localLibraryItem ? this.localLibraryItem.coverContentUrl : null
if (localMediaItemCover) return Capacitor.convertFileSrc(localMediaItemCover) if (localItemCover) return Capacitor.convertFileSrc(localItemCover)
return null return null
}, },
playMethod() { playMethod() {

View file

@ -1,8 +1,6 @@
<template> <template>
<div> <div>
<div id="streamContainer"> <app-audio-player ref="audioPlayer" :playing.sync="isPlaying" :bookmarks="bookmarks" :sleep-timer-running="isSleepTimerRunning" :sleep-time-remaining="sleepTimeRemaining" @selectPlaybackSpeed="showPlaybackSpeedModal = true" @updateTime="(t) => (currentTime = t)" @showSleepTimer="showSleepTimer" @showBookmarks="showBookmarks" />
<app-audio-player ref="audioPlayer" :playing.sync="isPlaying" :bookmarks="bookmarks" :sleep-timer-running="isSleepTimerRunning" :sleep-time-remaining="sleepTimeRemaining" @selectPlaybackSpeed="showPlaybackSpeedModal = true" @updateTime="(t) => (currentTime = t)" @showSleepTimer="showSleepTimer" @showBookmarks="showBookmarks" />
</div>
<modals-playback-speed-modal v-model="showPlaybackSpeedModal" :playback-rate.sync="playbackSpeed" @update:playbackRate="updatePlaybackSpeed" @change="changePlaybackSpeed" /> <modals-playback-speed-modal v-model="showPlaybackSpeedModal" :playback-rate.sync="playbackSpeed" @update:playbackRate="updatePlaybackSpeed" @change="changePlaybackSpeed" />
<modals-sleep-timer-modal v-model="showSleepTimerModal" :current-time="sleepTimeRemaining" :sleep-timer-running="isSleepTimerRunning" :current-end-of-chapter-time="currentEndOfChapterTime" @change="selectSleepTimeout" @cancel="cancelSleepTimer" @increase="increaseSleepTimer" @decrease="decreaseSleepTimer" /> <modals-sleep-timer-modal v-model="showSleepTimerModal" :current-time="sleepTimeRemaining" :sleep-timer-running="isSleepTimerRunning" :current-end-of-chapter-time="currentEndOfChapterTime" @change="selectSleepTimeout" @cancel="cancelSleepTimer" @increase="increaseSleepTimer" @decrease="decreaseSleepTimer" />

View file

@ -11,7 +11,7 @@
</button> </button>
<transition name="menu"> <transition name="menu">
<ul v-show="showMenu" class="absolute z-10 -mt-px w-full bg-primary border border-black-200 shadow-lg max-h-56 rounded-b-md py-1 ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" tabindex="-1" role="listbox"> <ul v-show="showMenu" class="absolute z-10 -mt-px w-full bg-primary border border-gray-600 shadow-lg max-h-56 rounded-b-md py-1 ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm" tabindex="-1" role="listbox">
<template v-for="item in items"> <template v-for="item in items">
<li :key="item.value" class="text-gray-100 select-none relative py-2 cursor-pointer hover:bg-black-400" id="listbox-option-0" role="option" @click="clickedOption(item.value)"> <li :key="item.value" class="text-gray-100 select-none relative py-2 cursor-pointer hover:bg-black-400" id="listbox-option-0" role="option" @click="clickedOption(item.value)">
<div class="flex items-center"> <div class="flex items-center">

View file

@ -97,7 +97,7 @@ export default {
var localCategories = await this.getLocalMediaItemCategories() var localCategories = await this.getLocalMediaItemCategories()
this.shelves = this.shelves.concat(localCategories) this.shelves = this.shelves.concat(localCategories)
if (this.user || !this.currentLibraryId) { 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) => {

View file

@ -3,7 +3,7 @@
<div class="flex"> <div class="flex">
<div class="w-32"> <div class="w-32">
<div class="relative"> <div class="relative">
<covers-book-cover :library-item="libraryItem" :download-cover="downloadedCover" :width="128" :book-cover-aspect-ratio="bookCoverAspectRatio" /> <covers-book-cover :library-item="libraryItem" :width="128" :book-cover-aspect-ratio="bookCoverAspectRatio" />
<div class="absolute bottom-0 left-0 h-1.5 bg-yellow-400 shadow-sm z-10" :style="{ width: 128 * progressPercent + 'px' }"></div> <div class="absolute bottom-0 left-0 h-1.5 bg-yellow-400 shadow-sm z-10" :style="{ width: 128 * progressPercent + 'px' }"></div>
</div> </div>
<div class="flex my-4"> <div class="flex my-4">

View file

@ -3,23 +3,24 @@
<div class="flex items-center mb-4"> <div class="flex items-center mb-4">
<div class="flex-grow" /> <div class="flex-grow" />
<ui-btn v-if="!removingFolder" :loading="isScanning" small @click="clickScan">Scan</ui-btn> <ui-btn v-if="!removingFolder" :loading="isScanning" small @click="clickScan">Scan</ui-btn>
<ui-btn v-if="!removingFolder && localMediaItems.length" :loading="isScanning" small class="ml-2" color="warning" @click="clickForceRescan">Force Re-Scan</ui-btn> <ui-btn v-if="!removingFolder && localLibraryItems.length" :loading="isScanning" small class="ml-2" color="warning" @click="clickForceRescan">Force Re-Scan</ui-btn>
<ui-icon-btn class="ml-2" bg-color="error" outlined :loading="removingFolder" icon="delete" @click="clickDeleteFolder" /> <ui-icon-btn class="ml-2" bg-color="error" outlined :loading="removingFolder" icon="delete" @click="clickDeleteFolder" />
</div> </div>
<p class="text-lg mb-0.5 text-white text-opacity-75">Folder: {{ folderName }}</p> <p class="text-lg mb-0.5 text-white text-opacity-75">Folder: {{ folderName }}</p>
<p class="mb-4 text-xl">Local Media Items ({{ localMediaItems.length }})</p> <p class="mb-4 text-xl">Local Library Items ({{ localLibraryItems.length }})</p>
<div v-if="isScanning" class="w-full text-center p-4"> <div v-if="isScanning" class="w-full text-center p-4">
<p>Scanning...</p> <p>Scanning...</p>
</div> </div>
<div v-else class="w-full media-item-container overflow-y-auto"> <div v-else class="w-full media-item-container overflow-y-auto">
<template v-for="mediaItem in localMediaItems"> <template v-for="mediaItem in localLibraryItems">
<div :key="mediaItem.id" class="flex my-1"> <div :key="mediaItem.id" class="flex my-1">
<div class="w-12 h-12 bg-primary"> <div class="w-12 h-12 bg-primary">
<img v-if="mediaItem.coverPathSrc" :src="mediaItem.coverPathSrc" class="w-full h-full object-contain" /> <img v-if="mediaItem.coverPathSrc" :src="mediaItem.coverPathSrc" class="w-full h-full object-contain" />
</div> </div>
<div class="flex-grow px-2"> <div class="flex-grow px-2">
<p>{{ mediaItem.name }}</p> <p>{{ mediaItem.media.metadata.title }}</p>
<p>{{ mediaItem.audioTracks.length }} Tracks</p> <p v-if="mediaItem.type == 'book'">{{ mediaItem.media.tracks.length }} Tracks</p>
<p v-else-if="mediaItem.type == 'podcast'">{{ mediaItem.media.episodes.length }} Tracks</p>
</div> </div>
<div class="w-12 h-12 flex items-center justify-center"> <div class="w-12 h-12 flex items-center justify-center">
<button v-if="!isMissing" class="shadow-sm text-accent flex items-center justify-center rounded-full" @click.stop="play(mediaItem)"> <button v-if="!isMissing" class="shadow-sm text-accent flex items-center justify-center rounded-full" @click.stop="play(mediaItem)">
@ -46,7 +47,7 @@ export default {
}, },
data() { data() {
return { return {
localMediaItems: [], localLibraryItems: [],
folder: null, folder: null,
isScanning: false, isScanning: false,
removingFolder: false removingFolder: false
@ -66,8 +67,8 @@ export default {
}, },
async clickDeleteFolder() { async clickDeleteFolder() {
var deleteMessage = 'Are you sure you want to remove this folder? (does not delete anything in your file system)' var deleteMessage = 'Are you sure you want to remove this folder? (does not delete anything in your file system)'
if (this.localMediaItems.length) { if (this.localLibraryItems.length) {
deleteMessage = `Are you sure you want to remove this folder and ${this.localMediaItems.length} media items? (does not delete anything in your file system)` deleteMessage = `Are you sure you want to remove this folder and ${this.localLibraryItems.length} items? (does not delete anything in your file system)`
} }
const { value } = await Dialog.confirm({ const { value } = await Dialog.confirm({
title: 'Confirm', title: 'Confirm',
@ -87,7 +88,7 @@ export default {
this.isScanning = true this.isScanning = true
var response = await AbsFileSystem.scanFolder({ folderId: this.folderId, forceAudioProbe }) var response = await AbsFileSystem.scanFolder({ folderId: this.folderId, forceAudioProbe })
if (response && response.localMediaItems) { if (response && response.localLibraryItems) {
var itemsAdded = response.itemsAdded var itemsAdded = response.itemsAdded
var itemsUpdated = response.itemsUpdated var itemsUpdated = response.itemsUpdated
var itemsRemoved = response.itemsRemoved var itemsRemoved = response.itemsRemoved
@ -100,14 +101,14 @@ export default {
this.$toast.info(`Folder scan complete:\n${toastMessages.join(' | ')}`) this.$toast.info(`Folder scan complete:\n${toastMessages.join(' | ')}`)
// When all items are up-to-date then local media items are not returned // When all items are up-to-date then local media items are not returned
if (response.localMediaItems.length) { if (response.localLibraryItems.length) {
this.localMediaItems = response.localMediaItems.map((mi) => { this.localLibraryItems = response.localLibraryItems.map((mi) => {
if (mi.coverContentUrl) { if (mi.coverContentUrl) {
mi.coverPathSrc = Capacitor.convertFileSrc(mi.coverContentUrl) mi.coverPathSrc = Capacitor.convertFileSrc(mi.coverContentUrl)
} }
return mi return mi
}) })
console.log('Set Local Media Items', this.localMediaItems.length) console.log('Set Local Media Items', this.localLibraryItems.length)
} }
} else { } else {
console.log('No Local media items found') console.log('No Local media items found')
@ -118,9 +119,9 @@ export default {
var folder = await this.$db.getLocalFolder(this.folderId) var folder = await this.$db.getLocalFolder(this.folderId)
this.folder = folder this.folder = folder
var items = (await this.$db.getLocalMediaItemsInFolder(this.folderId)) || [] var items = (await this.$db.getLocalLibraryItemsInFolder(this.folderId)) || []
console.log('Init folder', this.folderId, items) console.log('Init folder', this.folderId, items)
this.localMediaItems = items.map((lmi) => { this.localLibraryItems = items.map((lmi) => {
return { return {
...lmi, ...lmi,
coverPathSrc: lmi.coverContentUrl ? Capacitor.convertFileSrc(lmi.coverContentUrl) : null coverPathSrc: lmi.coverContentUrl ? Capacitor.convertFileSrc(lmi.coverContentUrl) : null

View file

@ -59,6 +59,7 @@ export default {
return this.$toast.error('Must select a media type') return this.$toast.error('Must select a media type')
} }
var folderObj = await AbsFileSystem.selectFolder({ mediaType: this.newFolderMediaType }) var folderObj = await AbsFileSystem.selectFolder({ mediaType: this.newFolderMediaType })
if (!folderObj) return
if (folderObj.error) { if (folderObj.error) {
return this.$toast.error(`Error: ${folderObj.error || 'Unknown Error'}`) return this.$toast.error(`Error: ${folderObj.error || 'Unknown Error'}`)
} }

View file

@ -4,6 +4,8 @@ class AbsFileSystemWeb extends WebPlugin {
constructor() { constructor() {
super() super()
} }
async selectFolder() { }
} }
const AbsFileSystem = registerPlugin('AbsFileSystem', { const AbsFileSystem = registerPlugin('AbsFileSystem', {

View file

@ -73,14 +73,14 @@ class DbService {
}) })
} }
getLocalMediaItemsInFolder(folderId) { getLocalLibraryItemsInFolder(folderId) {
if (isWeb) return [] if (isWeb) return []
return AbsDatabase.getLocalMediaItemsInFolder({ folderId }).then((data) => { return AbsDatabase.getLocalLibraryItemsInFolder({ folderId }).then((data) => {
console.log('Loaded local media items in folder', JSON.stringify(data)) console.log('Loaded local library items in folder', JSON.stringify(data))
if (data.localMediaItems && typeof data.localMediaItems == 'string') { if (data.localLibraryItems && typeof data.localLibraryItems == 'string') {
return JSON.parse(data.localMediaItems) return JSON.parse(data.localLibraryItems)
} }
return data.localMediaItems return data.localLibraryItems
}) })
} }

View file

@ -38,7 +38,7 @@ export const getters = {
return state.serverSettings[key] return state.serverSettings[key]
}, },
getBookCoverAspectRatio: state => { getBookCoverAspectRatio: state => {
if (!state.serverSettings || !state.serverSettings.coverAspectRatio) return 1.6 if (!state.serverSettings || !state.serverSettings.coverAspectRatio) return 1
return state.serverSettings.coverAspectRatio === 0 ? 1.6 : 1 return state.serverSettings.coverAspectRatio === 0 ? 1.6 : 1
}, },
} }