mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-07 04:18:48 +02:00
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:
@@ -71,7 +71,7 @@
|
||||
<service
|
||||
android:exported="true"
|
||||
android:enabled="true"
|
||||
android:name=".PlayerNotificationService">
|
||||
android:name=".player.PlayerNotificationService">
|
||||
<intent-filter>
|
||||
<action android:name="android.media.browse.MediaBrowserService"/>
|
||||
</intent-filter>
|
||||
|
||||
@@ -10,7 +10,6 @@ import androidx.core.app.ActivityCompat
|
||||
import com.anggrayudi.storage.SimpleStorage
|
||||
import com.anggrayudi.storage.SimpleStorageHelper
|
||||
import com.audiobookshelf.app.data.AbsDatabase
|
||||
import com.audiobookshelf.app.data.DbManager
|
||||
import com.audiobookshelf.app.player.PlayerNotificationService
|
||||
import com.audiobookshelf.app.plugins.AbsDownloader
|
||||
import com.audiobookshelf.app.plugins.AbsAudioPlayer
|
||||
@@ -18,7 +17,6 @@ import com.audiobookshelf.app.plugins.AbsFileSystem
|
||||
import com.getcapacitor.BridgeActivity
|
||||
import io.paperdb.Paper
|
||||
|
||||
|
||||
class MainActivity : BridgeActivity() {
|
||||
private val tag = "MainActivity"
|
||||
|
||||
@@ -87,6 +85,7 @@ class MainActivity : BridgeActivity() {
|
||||
|
||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||
super.onPostCreate(savedInstanceState)
|
||||
Log.d(tag, "onPostCreate MainActivity")
|
||||
|
||||
mConnection = object : ServiceConnection {
|
||||
override fun onServiceDisconnected(name: ComponentName) {
|
||||
@@ -97,7 +96,6 @@ class MainActivity : BridgeActivity() {
|
||||
override fun onServiceConnected(name: ComponentName, service: IBinder) {
|
||||
Log.d(tag, "Service Connected $name")
|
||||
|
||||
|
||||
mBounded = true
|
||||
val mLocalBinder = service as PlayerNotificationService.LocalBinder
|
||||
foregroundService = mLocalBinder.getService()
|
||||
@@ -109,8 +107,10 @@ class MainActivity : BridgeActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
val startIntent = Intent(this, PlayerNotificationService::class.java)
|
||||
bindService(startIntent, mConnection as ServiceConnection, Context.BIND_AUTO_CREATE);
|
||||
Intent(this, PlayerNotificationService::class.java).also { intent ->
|
||||
Log.d(tag, "Binding PlayerNotificationService")
|
||||
bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -30,28 +30,68 @@ data class LibraryItem(
|
||||
JsonSubTypes.Type(Book::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)
|
||||
data class Podcast(
|
||||
var metadata:PodcastMetadata,
|
||||
var coverPath:String?,
|
||||
class Podcast(
|
||||
metadata:PodcastMetadata,
|
||||
coverPath:String?,
|
||||
var tags:MutableList<String>,
|
||||
var episodes:MutableList<PodcastEpisode>,
|
||||
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)
|
||||
data class Book(
|
||||
var metadata:BookMetadata,
|
||||
var coverPath:String?,
|
||||
class Book(
|
||||
metadata:BookMetadata,
|
||||
coverPath:String?,
|
||||
var tags:List<String>,
|
||||
var audioFiles:List<AudioFile>,
|
||||
var chapters:List<BookChapter>,
|
||||
var tracks:List<AudioTrack>?,
|
||||
var size:Long?,
|
||||
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
|
||||
@JsonTypeInfo(use=JsonTypeInfo.Id.DEDUCTION)
|
||||
@@ -59,11 +99,11 @@ data class Book(
|
||||
JsonSubTypes.Type(BookMetadata::class),
|
||||
JsonSubTypes.Type(PodcastMetadata::class)
|
||||
)
|
||||
open class MediaTypeMetadata {}
|
||||
open class MediaTypeMetadata(var title:String) {}
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class BookMetadata(
|
||||
var title:String,
|
||||
class BookMetadata(
|
||||
title:String,
|
||||
var subtitle:String?,
|
||||
var authors:MutableList<Author>,
|
||||
var narrators:MutableList<String>,
|
||||
@@ -81,15 +121,15 @@ data class BookMetadata(
|
||||
var authorNameLF:String?,
|
||||
var narratorName:String?,
|
||||
var seriesName:String?
|
||||
) : MediaTypeMetadata()
|
||||
) : MediaTypeMetadata(title)
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class PodcastMetadata(
|
||||
var title:String,
|
||||
class PodcastMetadata(
|
||||
title:String,
|
||||
var author:String?,
|
||||
var feedUrl:String?,
|
||||
var genres:MutableList<String>
|
||||
) : MediaTypeMetadata()
|
||||
) : MediaTypeMetadata(title)
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class Author(
|
||||
@@ -107,7 +147,8 @@ data class PodcastEpisode(
|
||||
var title:String?,
|
||||
var subtitle:String?,
|
||||
var description:String?,
|
||||
var audioFile:AudioFile
|
||||
var audioFile:AudioFile?,
|
||||
var audioTrack:AudioTrack?
|
||||
)
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
|
||||
@@ -14,11 +14,11 @@ class DbManager {
|
||||
Paper.book("device").write("data", deviceData)
|
||||
}
|
||||
|
||||
fun getLocalMediaItems():MutableList<LocalMediaItem> {
|
||||
var localMediaItems:MutableList<LocalMediaItem> = mutableListOf()
|
||||
Paper.book("localMediaItems").allKeys.forEach {
|
||||
var localMediaItem:LocalMediaItem? = Paper.book("localMediaItems").read(it)
|
||||
if (localMediaItem != null) {
|
||||
fun getLocalLibraryItems():MutableList<LocalLibraryItem> {
|
||||
var localLibraryItems:MutableList<LocalLibraryItem> = mutableListOf()
|
||||
Paper.book("localLibraryItems").allKeys.forEach {
|
||||
var localLibraryItem:LocalLibraryItem? = Paper.book("localLibraryItems").read(it)
|
||||
if (localLibraryItem != null) {
|
||||
// TODO: Check to make sure all file paths exist
|
||||
// if (localMediaItem.coverContentUrl != null) {
|
||||
// var file = DocumentFile.fromSingleUri(ctx)
|
||||
@@ -29,31 +29,31 @@ class DbManager {
|
||||
// localMediaItems.add(localMediaItem)
|
||||
// }
|
||||
// } else {
|
||||
localMediaItems.add(localMediaItem)
|
||||
localLibraryItems.add(localLibraryItem)
|
||||
// }
|
||||
}
|
||||
}
|
||||
return localMediaItems
|
||||
return localLibraryItems
|
||||
}
|
||||
|
||||
fun getLocalMediaItemsInFolder(folderId:String):List<LocalMediaItem> {
|
||||
var localMediaItems = getLocalMediaItems()
|
||||
return localMediaItems.filter {
|
||||
fun getLocalLibraryItemsInFolder(folderId:String):List<LocalLibraryItem> {
|
||||
var localLibraryItems = getLocalLibraryItems()
|
||||
return localLibraryItems.filter {
|
||||
it.folderId == folderId
|
||||
}
|
||||
}
|
||||
|
||||
fun getLocalMediaItem(localMediaItemId:String):LocalMediaItem? {
|
||||
return Paper.book("localMediaItems").read(localMediaItemId)
|
||||
fun getLocalLibraryItem(localLibraryItemId:String):LocalLibraryItem? {
|
||||
return Paper.book("localLibraryItems").read(localLibraryItemId)
|
||||
}
|
||||
|
||||
fun removeLocalMediaItem(localMediaItemId:String) {
|
||||
Paper.book("localMediaItems").delete(localMediaItemId)
|
||||
fun removeLocalLibraryItem(localLibraryItemId:String) {
|
||||
Paper.book("localLibraryItems").delete(localLibraryItemId)
|
||||
}
|
||||
|
||||
fun saveLocalMediaItems(localMediaItems:List<LocalMediaItem>) {
|
||||
localMediaItems.map {
|
||||
Paper.book("localMediaItems").write(it.id, it)
|
||||
fun saveLocalLibraryItems(localLibraryItems:List<LocalLibraryItem>) {
|
||||
localLibraryItems.map {
|
||||
Paper.book("localLibraryItems").write(it.id, it)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,9 +77,9 @@ class DbManager {
|
||||
}
|
||||
|
||||
fun removeLocalFolder(folderId:String) {
|
||||
var localMediaItems = getLocalMediaItemsInFolder(folderId)
|
||||
localMediaItems.forEach {
|
||||
Paper.book("localMediaItems").delete(it.id)
|
||||
var localLibraryItems = getLocalLibraryItemsInFolder(folderId)
|
||||
localLibraryItems.forEach {
|
||||
Paper.book("localLibraryItems").delete(it.id)
|
||||
}
|
||||
Paper.book("localFolders").delete(folderId)
|
||||
}
|
||||
|
||||
@@ -22,14 +22,54 @@ data class DeviceData(
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class LocalLibraryItem(
|
||||
var id:String,
|
||||
var libraryItemId:String?,
|
||||
var folderId:String,
|
||||
var absolutePath:String,
|
||||
var isInvalid:Boolean,
|
||||
var mediaType:String,
|
||||
var media:MediaType,
|
||||
var localFiles:MutableList<LocalFile>,
|
||||
var coverContentUrl:String?,
|
||||
var coverAbsolutePath:String?,
|
||||
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)
|
||||
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
|
||||
fun getAudiobookChapters():List<BookChapter> {
|
||||
if (mediaType != "book" || audioTracks.isEmpty()) return mutableListOf()
|
||||
@@ -98,11 +124,11 @@ data class LocalMediaItem(
|
||||
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)
|
||||
var book = Book(mediaMetadata as BookMetadata, coverAbsolutePath, mutableListOf(), mutableListOf(), chapters,audioTracks,getTotalSize(),getDuration())
|
||||
return LocalLibraryItem(id, null, folderId, absolutePath, false,mediaType, book, localFiles, coverContentUrl, coverAbsolutePath,true)
|
||||
} else {
|
||||
var podcast = Podcast(mediaMetadata as PodcastMetadata, coverContentUrl, mutableListOf(), mutableListOf(), false)
|
||||
return LocalLibraryItem(id, folderId, absolutePath, false, mediaType, podcast,localFiles,true)
|
||||
var podcast = Podcast(mediaMetadata as PodcastMetadata, coverAbsolutePath, mutableListOf(), mutableListOf(), false)
|
||||
return LocalLibraryItem(id, null, folderId, absolutePath, false, mediaType, podcast,localFiles,coverContentUrl, coverAbsolutePath, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,5 +6,5 @@ data class FolderScanResult(
|
||||
var itemsRemoved:Int,
|
||||
var itemsUpToDate:Int,
|
||||
val localFolder:LocalFolder,
|
||||
val localMediaItems:List<LocalMediaItem>,
|
||||
val localLibraryItems:List<LocalLibraryItem>,
|
||||
)
|
||||
|
||||
@@ -32,7 +32,7 @@ class PlaybackSession(
|
||||
var audioTracks:MutableList<AudioTrack>,
|
||||
var currentTime:Double,
|
||||
var libraryItem:LibraryItem?,
|
||||
var localMediaItem:LocalMediaItem?,
|
||||
var localLibraryItem:LocalLibraryItem?,
|
||||
var serverUrl:String?,
|
||||
var token:String?
|
||||
) {
|
||||
@@ -74,7 +74,7 @@ class PlaybackSession(
|
||||
|
||||
@JsonIgnore
|
||||
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)
|
||||
return Uri.parse("$serverUrl/api/items/$libraryItemId/cover?token=$token")
|
||||
|
||||
@@ -15,6 +15,10 @@ import com.fasterxml.jackson.module.kotlin.readValue
|
||||
class FolderScanner(var ctx: Context) {
|
||||
private val tag = "FolderScanner"
|
||||
|
||||
private fun getLocalLibraryItemId(mediaItemId:String):String {
|
||||
return "local_" + DeviceManager.getBase64Id(mediaItemId)
|
||||
}
|
||||
|
||||
// TODO: CLEAN this monster! Divide into bite-size methods
|
||||
fun scanForMediaItems(localFolder:LocalFolder, forceAudioProbe:Boolean):FolderScanResult? {
|
||||
FFmpegKitConfig.enableLogCallback { log ->
|
||||
@@ -38,32 +42,32 @@ class FolderScanner(var ctx: Context) {
|
||||
// Search for files in media item folder
|
||||
var foldersFound = df.search(false, DocumentFileType.FOLDER)
|
||||
|
||||
// Match folders found with media items already saved in db
|
||||
var existingMediaItems = DeviceManager.dbManager.getLocalMediaItemsInFolder(localFolder.id)
|
||||
// Match folders found with local library items already saved in db
|
||||
var existingLocalLibraryItems = DeviceManager.dbManager.getLocalLibraryItemsInFolder(localFolder.id)
|
||||
|
||||
// Remove existing items no longer there
|
||||
existingMediaItems = existingMediaItems.filter { lmi ->
|
||||
var fileFound = foldersFound.find { f -> lmi.id == DeviceManager.getBase64Id(f.id) }
|
||||
existingLocalLibraryItems = existingLocalLibraryItems.filter { lli ->
|
||||
var fileFound = foldersFound.find { f -> lli.id == getLocalLibraryItemId(f.id) }
|
||||
if (fileFound == null) {
|
||||
Log.d(tag, "Existing media item is no longer in file system ${lmi.name}")
|
||||
DeviceManager.dbManager.removeLocalMediaItem(lmi.id)
|
||||
Log.d(tag, "Existing local library item is no longer in file system ${lli.media.metadata.title}")
|
||||
DeviceManager.dbManager.removeLocalLibraryItem(lli.id)
|
||||
mediaItemsRemoved++
|
||||
}
|
||||
fileFound != null
|
||||
}
|
||||
|
||||
var mediaItems = mutableListOf<LocalMediaItem>()
|
||||
var localLibraryItems = mutableListOf<LocalLibraryItem>()
|
||||
|
||||
foldersFound.forEach {
|
||||
Log.d(tag, "Iterating over Folder Found ${it.name} | ${it.getSimplePath(ctx)} | URI: ${it.uri}")
|
||||
foldersFound.forEach { itemFolder ->
|
||||
Log.d(tag, "Iterating over Folder Found ${itemFolder.name} | ${itemFolder.getSimplePath(ctx)} | URI: ${itemFolder.uri}")
|
||||
|
||||
var itemFolderName = it.name ?: ""
|
||||
var itemId = "local_" + DeviceManager.getBase64Id(it.id)
|
||||
var itemFolderName = itemFolder.name ?: ""
|
||||
var itemId = getLocalLibraryItemId(itemFolder.id)
|
||||
|
||||
var existingMediaItem = existingMediaItems.find { emi -> emi.id == itemId }
|
||||
var existingLocalFiles = existingMediaItem?.localFiles ?: mutableListOf()
|
||||
var existingAudioTracks = existingMediaItem?.audioTracks ?: mutableListOf()
|
||||
var isNewOrUpdated = existingMediaItem == null
|
||||
var existingItem = existingLocalLibraryItems.find { emi -> emi.id == itemId }
|
||||
var existingLocalFiles = existingItem?.localFiles ?: mutableListOf()
|
||||
var existingAudioTracks = existingItem?.media?.getAudioTracks() ?: mutableListOf()
|
||||
var isNewOrUpdated = existingItem == null
|
||||
|
||||
var audioTracks = mutableListOf<AudioTrack>()
|
||||
var localFiles = mutableListOf<LocalFile>()
|
||||
@@ -72,13 +76,14 @@ class FolderScanner(var ctx: Context) {
|
||||
var coverContentUrl: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 ->
|
||||
filesInFolder.find { fif -> DeviceManager.getBase64Id(fif.id) == elf.id } == null // File was not found in media item folder
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -147,9 +152,12 @@ class FolderScanner(var ctx: Context) {
|
||||
if (existingLocalFile == null) {
|
||||
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
|
||||
isNewOrUpdated = true
|
||||
existingItem.coverAbsolutePath = localFile.absolutePath
|
||||
existingItem.coverContentUrl = localFile.contentUrl
|
||||
existingItem.media.coverPath = localFile.absolutePath
|
||||
}
|
||||
|
||||
// First image file use as cover path
|
||||
@@ -160,30 +168,36 @@ class FolderScanner(var ctx: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
if (existingMediaItem != null && audioTracks.isEmpty()) {
|
||||
Log.d(tag, "Local media item ${existingMediaItem.name} no longer has audio tracks - removing item")
|
||||
DeviceManager.dbManager.removeLocalMediaItem(existingMediaItem.id)
|
||||
if (existingItem != null && audioTracks.isEmpty()) {
|
||||
Log.d(tag, "Local library item ${existingItem.media.metadata.title} no longer has audio tracks - removing item")
|
||||
DeviceManager.dbManager.removeLocalLibraryItem(existingItem.id)
|
||||
mediaItemsRemoved++
|
||||
} else if (existingMediaItem != null && !isNewOrUpdated) {
|
||||
Log.d(tag, "Local media item ${existingMediaItem.name} has no updates")
|
||||
} else if (existingItem != null && !isNewOrUpdated) {
|
||||
Log.d(tag, "Local library item ${existingItem.media.metadata.title} has no updates")
|
||||
mediaItemsUpToDate++
|
||||
} else if (audioTracks.isNotEmpty()) {
|
||||
if (existingMediaItem != null) mediaItemsUpdated++
|
||||
else mediaItemsAdded++
|
||||
} else if (existingItem != null) {
|
||||
Log.d(tag, "Updating local library item ${existingItem.media.metadata.title}")
|
||||
mediaItemsUpdated++
|
||||
|
||||
Log.d(tag, "Found local media item named $itemFolderName with ${audioTracks.size} tracks and ${localFiles.size} local files")
|
||||
var localMediaItem = LocalMediaItem(itemId, itemFolderName, localFolder.mediaType, localFolder.id, it.uri.toString(), it.getSimplePath(ctx), it.getAbsolutePath(ctx),audioTracks,localFiles,coverContentUrl,coverAbsolutePath)
|
||||
mediaItems.add(localMediaItem)
|
||||
existingItem.updateFromScan(audioTracks,localFiles)
|
||||
localLibraryItems.add(existingItem)
|
||||
} 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")
|
||||
|
||||
return if (mediaItems.isNotEmpty()) {
|
||||
DeviceManager.dbManager.saveLocalMediaItems(mediaItems)
|
||||
return if (localLibraryItems.isNotEmpty()) {
|
||||
DeviceManager.dbManager.saveLocalLibraryItems(localLibraryItems)
|
||||
|
||||
var folderMediaItems = DeviceManager.dbManager.getLocalMediaItemsInFolder(localFolder.id) // Get all local media items
|
||||
FolderScanResult(mediaItemsAdded, mediaItemsUpdated, mediaItemsRemoved, mediaItemsUpToDate, localFolder, folderMediaItems)
|
||||
var folderLibraryItems = DeviceManager.dbManager.getLocalLibraryItemsInFolder(localFolder.id) // Get all local media items
|
||||
FolderScanResult(mediaItemsAdded, mediaItemsUpdated, mediaItemsRemoved, mediaItemsUpToDate, localFolder, folderLibraryItems)
|
||||
} else {
|
||||
Log.d(tag, "No Media Items to save")
|
||||
FolderScanResult(mediaItemsAdded, mediaItemsUpdated, mediaItemsRemoved, mediaItemsUpToDate, localFolder, mutableListOf())
|
||||
|
||||
+1
-1
@@ -133,8 +133,8 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
Log.d(tag, "onStartCommand $startId")
|
||||
isStarted = true
|
||||
Log.d(tag, "onStartCommand $startId")
|
||||
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package com.audiobookshelf.app.plugins
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.audiobookshelf.app.MainActivity
|
||||
import com.audiobookshelf.app.data.PlaybackSession
|
||||
import com.audiobookshelf.app.device.DeviceManager
|
||||
@@ -75,11 +77,19 @@ class AbsAudioPlayer : Plugin() {
|
||||
|
||||
@PluginMethod
|
||||
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 playWhenReady = call.getBoolean("playWhenReady") == true
|
||||
|
||||
if (libraryItemId.startsWith("local")) { // Play local media item
|
||||
DeviceManager.dbManager.getLocalMediaItem(libraryItemId)?.let {
|
||||
DeviceManager.dbManager.getLocalLibraryItem(libraryItemId)?.let {
|
||||
Handler(Looper.getMainLooper()).post() {
|
||||
Log.d(tag, "Preparing Local Media item ${jacksonObjectMapper().writeValueAsString(it)}")
|
||||
var playbackSession = it.getPlaybackSession()
|
||||
|
||||
@@ -48,23 +48,22 @@ class AbsDatabase : Plugin() {
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
fun getLocalMediaItemsInFolder(call:PluginCall) {
|
||||
var folderId = call.getString("folderId", "").toString()
|
||||
fun getLocalLibraryItem(call:PluginCall) {
|
||||
var id = call.getString("id", "").toString()
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
var localMediaItems = DeviceManager.dbManager.getLocalMediaItemsInFolder(folderId)
|
||||
var mediaItemsArray = jacksonObjectMapper().writeValueAsString(localMediaItems)
|
||||
var jsobj = JSObject()
|
||||
jsobj.put("localMediaItems", mediaItemsArray)
|
||||
call.resolve(jsobj)
|
||||
var localLibraryItem = DeviceManager.dbManager.getLocalLibraryItem(id)
|
||||
if (localLibraryItem == null) {
|
||||
call.resolve()
|
||||
} else {
|
||||
call.resolve(JSObject(jacksonObjectMapper().writeValueAsString(localLibraryItem)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
fun getLocalLibraryItems(call:PluginCall) {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
var localLibraryItems = DeviceManager.dbManager.getLocalMediaItems().map {
|
||||
it.getLocalLibraryItem()
|
||||
}
|
||||
var localLibraryItems = DeviceManager.dbManager.getLocalLibraryItems()
|
||||
var jsobj = JSObject()
|
||||
jsobj.put("localLibraryItems", jacksonObjectMapper().writeValueAsString(localLibraryItems))
|
||||
call.resolve(jsobj)
|
||||
@@ -72,16 +71,14 @@ class AbsDatabase : Plugin() {
|
||||
}
|
||||
|
||||
@PluginMethod
|
||||
fun getLocalLibraryItem(call:PluginCall) {
|
||||
var id = call.getString("id", "").toString()
|
||||
fun getLocalLibraryItemsInFolder(call:PluginCall) {
|
||||
var folderId = call.getString("folderId", "").toString()
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
var mediaItem = DeviceManager.dbManager.getLocalMediaItem(id)
|
||||
var localLibraryItem = mediaItem?.getLocalLibraryItem()
|
||||
if (localLibraryItem == null) {
|
||||
call.resolve()
|
||||
} else {
|
||||
call.resolve(JSObject(jacksonObjectMapper().writeValueAsString(localLibraryItem)))
|
||||
}
|
||||
var localMediaItems = DeviceManager.dbManager.getLocalLibraryItemsInFolder(folderId)
|
||||
var mediaItemsArray = jacksonObjectMapper().writeValueAsString(localMediaItems)
|
||||
var jsobj = JSObject()
|
||||
jsobj.put("localLibraryItems", mediaItemsArray)
|
||||
call.resolve(jsobj)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,11 +118,11 @@ class AbsDownloader : Plugin() {
|
||||
fun getAbMetadataText(libraryItem:LibraryItem):String {
|
||||
var bookMedia = libraryItem.media as com.audiobookshelf.app.data.Book
|
||||
var fileString = ";ABMETADATA1\n"
|
||||
fileString += "#libraryItemId=${libraryItem.id}\n"
|
||||
fileString += "title=${bookMedia.metadata.title}\n"
|
||||
fileString += "author=${bookMedia.metadata.authorName}\n"
|
||||
fileString += "narrator=${bookMedia.metadata.narratorName}\n"
|
||||
fileString += "series=${bookMedia.metadata.seriesName}\n"
|
||||
// fileString += "#libraryItemId=${libraryItem.id}\n"
|
||||
// fileString += "title=${bookMedia.metadata.title}\n"
|
||||
// fileString += "author=${bookMedia.metadata.authorName}\n"
|
||||
// fileString += "narrator=${bookMedia.metadata.narratorName}\n"
|
||||
// fileString += "series=${bookMedia.metadata.seriesName}\n"
|
||||
return fileString
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user