Auto formatting AbsDownloader.kt

This commit is contained in:
Nicholas Wallace
2025-02-28 20:37:08 -07:00
parent feef527bed
commit ee0ab43fc5
@@ -8,10 +8,10 @@ import com.audiobookshelf.app.MainActivity
import com.audiobookshelf.app.data.*
import com.audiobookshelf.app.device.DeviceManager
import com.audiobookshelf.app.device.FolderScanner
import com.audiobookshelf.app.managers.DownloadItemManager
import com.audiobookshelf.app.models.DownloadItem
import com.audiobookshelf.app.models.DownloadItemPart
import com.audiobookshelf.app.server.ApiHandler
import com.audiobookshelf.app.managers.DownloadItemManager
import com.fasterxml.jackson.core.json.JsonReadFeature
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.getcapacitor.JSObject
@@ -24,7 +24,9 @@ import java.io.File
@CapacitorPlugin(name = "AbsDownloader")
class AbsDownloader : Plugin() {
private val tag = "AbsDownloader"
private var jacksonMapper = jacksonObjectMapper().enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
private var jacksonMapper =
jacksonObjectMapper()
.enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
lateinit var mainActivity: MainActivity
lateinit var downloadManager: DownloadManager
@@ -32,24 +34,32 @@ class AbsDownloader : Plugin() {
lateinit var folderScanner: FolderScanner
lateinit var downloadItemManager: DownloadItemManager
private val clientEventEmitter = (object : DownloadItemManager.DownloadEventEmitter {
override fun onDownloadItem(downloadItem:DownloadItem) {
notifyListeners("onDownloadItem", JSObject(jacksonMapper.writeValueAsString(downloadItem)))
}
override fun onDownloadItemPartUpdate(downloadItemPart:DownloadItemPart) {
notifyListeners("onDownloadItemPartUpdate", JSObject(jacksonMapper.writeValueAsString(downloadItemPart)))
}
override fun onDownloadItemComplete(jsobj:JSObject) {
notifyListeners("onItemDownloadComplete", jsobj)
}
})
private val clientEventEmitter =
(object : DownloadItemManager.DownloadEventEmitter {
override fun onDownloadItem(downloadItem: DownloadItem) {
notifyListeners(
"onDownloadItem",
JSObject(jacksonMapper.writeValueAsString(downloadItem))
)
}
override fun onDownloadItemPartUpdate(downloadItemPart: DownloadItemPart) {
notifyListeners(
"onDownloadItemPartUpdate",
JSObject(jacksonMapper.writeValueAsString(downloadItemPart))
)
}
override fun onDownloadItemComplete(jsobj: JSObject) {
notifyListeners("onItemDownloadComplete", jsobj)
}
})
override fun load() {
mainActivity = (activity as MainActivity)
downloadManager = activity.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
folderScanner = FolderScanner(mainActivity)
apiHandler = ApiHandler(mainActivity)
downloadItemManager = DownloadItemManager(downloadManager, folderScanner, mainActivity, clientEventEmitter)
downloadItemManager =
DownloadItemManager(downloadManager, folderScanner, mainActivity, clientEventEmitter)
}
@PluginMethod
@@ -58,12 +68,17 @@ class AbsDownloader : Plugin() {
var episodeId = call.data.getString("episodeId").toString()
if (episodeId == "null") episodeId = ""
var localFolderId = call.data.getString("localFolderId", "").toString()
Log.d(tag, "Download library item $libraryItemId to folder $localFolderId / episode: $episodeId")
Log.d(
tag,
"Download library item $libraryItemId to folder $localFolderId / episode: $episodeId"
)
val downloadId = if (episodeId.isEmpty()) libraryItemId else "$libraryItemId-$episodeId"
if (downloadItemManager.downloadItemQueue.find { it.id == downloadId } != null) {
Log.d(tag, "Download already started for this media entity $downloadId")
return call.resolve(JSObject("{\"error\":\"Download already started for this media entity\"}"))
return call.resolve(
JSObject("{\"error\":\"Download already started for this media entity\"}")
)
}
apiHandler.getLibraryItemWithProgress(libraryItemId, episodeId) { libraryItem ->
@@ -79,7 +94,17 @@ class AbsDownloader : Plugin() {
if (localFolder == null && localFolderId.startsWith("internal-")) {
Log.d(tag, "Creating new App Storage internal LocalFolder $localFolderId")
localFolder = LocalFolder(localFolderId, "Internal App Storage", "", "", "", "", "internal", libraryItem.mediaType)
localFolder =
LocalFolder(
localFolderId,
"Internal App Storage",
"",
"",
"",
"",
"internal",
libraryItem.mediaType
)
DeviceManager.dbManager.saveLocalFolder(localFolder)
}
@@ -89,9 +114,8 @@ class AbsDownloader : Plugin() {
call.resolve(JSObject("{\"error\":\"Invalid library item not a podcast\"}"))
} else if (episodeId.isNotEmpty()) {
val podcast = libraryItem.media as Podcast
val episode = podcast.episodes?.find { podcastEpisode ->
podcastEpisode.id == episodeId
}
val episode =
podcast.episodes?.find { podcastEpisode -> podcastEpisode.id == episodeId }
if (episode == null) {
call.resolve(JSObject("{\"error\":\"Invalid podcast episode not found\"}"))
} else {
@@ -118,21 +142,25 @@ class AbsDownloader : Plugin() {
// Replace characters that cant be used in the file system
// Reserved characters: ?:\"*|/\\<>
private fun cleanStringForFileSystem(str:String):String {
private fun cleanStringForFileSystem(str: String): String {
val reservedCharacters = listOf("?", "\"", "*", "|", "/", "\\", "<", ">")
var newTitle = str
newTitle = newTitle.replace(":", " -") // Special case replace : with -
reservedCharacters.forEach {
newTitle = newTitle.replace(it, "")
}
reservedCharacters.forEach { newTitle = newTitle.replace(it, "") }
return newTitle
}
private fun startLibraryItemDownload(libraryItem: LibraryItem, localFolder: LocalFolder, episode:PodcastEpisode?) {
private fun startLibraryItemDownload(
libraryItem: LibraryItem,
localFolder: LocalFolder,
episode: PodcastEpisode?
) {
val isInternal = localFolder.id.startsWith("internal-")
val tempFolderPath = if (isInternal) "${mainActivity.filesDir}/downloads/${libraryItem.id}" else mainActivity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
val tempFolderPath =
if (isInternal) "${mainActivity.filesDir}/downloads/${libraryItem.id}"
else mainActivity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
Log.d(tag, "downloadCacheDirectory=$tempFolderPath")
@@ -143,8 +171,25 @@ class AbsDownloader : Plugin() {
val tracks = libraryItem.media.getAudioTracks()
Log.d(tag, "Starting library item download with ${tracks.size} tracks")
val itemSubfolder = "$bookAuthor/$bookTitle"
val itemFolderPath = if (isInternal) "$tempFolderPath" else "${localFolder.absolutePath}/$itemSubfolder"
val downloadItem = DownloadItem(libraryItem.id, libraryItem.id, null, libraryItem.userMediaProgress,DeviceManager.serverConnectionConfig?.id ?: "", DeviceManager.serverAddress, DeviceManager.serverUserId, libraryItem.mediaType, itemFolderPath, localFolder, bookTitle, itemSubfolder, libraryItem.media, mutableListOf())
val itemFolderPath =
if (isInternal) "$tempFolderPath" else "${localFolder.absolutePath}/$itemSubfolder"
val downloadItem =
DownloadItem(
libraryItem.id,
libraryItem.id,
null,
libraryItem.userMediaProgress,
DeviceManager.serverConnectionConfig?.id ?: "",
DeviceManager.serverAddress,
DeviceManager.serverUserId,
libraryItem.mediaType,
itemFolderPath,
localFolder,
bookTitle,
itemSubfolder,
libraryItem.media,
mutableListOf()
)
val book = libraryItem.media as Book
book.ebookFile?.let { ebookFile ->
@@ -155,16 +200,35 @@ class AbsDownloader : Plugin() {
val destinationFile = File("$tempFolderPath/$destinationFilename")
if (destinationFile.exists()) {
Log.d(tag, "TEMP ebook file already exists, removing it from ${destinationFile.absolutePath}")
Log.d(
tag,
"TEMP ebook file already exists, removing it from ${destinationFile.absolutePath}"
)
destinationFile.delete()
}
if (finalDestinationFile.exists()) {
Log.d(tag, "ebook file already exists, removing it from ${finalDestinationFile.absolutePath}")
Log.d(
tag,
"ebook file already exists, removing it from ${finalDestinationFile.absolutePath}"
)
finalDestinationFile.delete()
}
val downloadItemPart = DownloadItemPart.make(downloadItem.id, destinationFilename, fileSize, destinationFile,finalDestinationFile,itemSubfolder,serverPath,localFolder,ebookFile,null,null)
val downloadItemPart =
DownloadItemPart.make(
downloadItem.id,
destinationFilename,
fileSize,
destinationFile,
finalDestinationFile,
itemSubfolder,
serverPath,
localFolder,
ebookFile,
null,
null
)
downloadItem.downloadItemParts.add(downloadItemPart)
}
@@ -173,34 +237,59 @@ class AbsDownloader : Plugin() {
tracks.forEach { audioTrack ->
val fileSize = audioTrack.metadata?.size ?: 0
// TODO: Currently file ino is only stored on AudioFile. This should be updated server side to be in FileMetadata or on the AudioTrack
// TODO: Currently file ino is only stored on AudioFile. This should be updated server side
// to be in FileMetadata or on the AudioTrack
val audioFileIno = audioFiles.find { it.metadata.path == audioTrack.metadata?.path }?.ino
val serverPath = "/api/items/${libraryItem.id}/file/${audioFileIno}/download"
val destinationFilename = getFilenameFromRelPath(audioTrack.relPath)
Log.d(tag, "Audio File Server Path $serverPath | AF RelPath ${audioTrack.relPath} | LocalFolder Path ${localFolder.absolutePath} | DestName $destinationFilename")
Log.d(
tag,
"Audio File Server Path $serverPath | AF RelPath ${audioTrack.relPath} | LocalFolder Path ${localFolder.absolutePath} | DestName $destinationFilename"
)
val finalDestinationFile = File("$itemFolderPath/$destinationFilename")
val destinationFile = File("$tempFolderPath/$destinationFilename")
if (destinationFile.exists()) {
Log.d(tag, "TEMP Audio file already exists, removing it from ${destinationFile.absolutePath}")
Log.d(
tag,
"TEMP Audio file already exists, removing it from ${destinationFile.absolutePath}"
)
destinationFile.delete()
}
if (finalDestinationFile.exists()) {
Log.d(tag, "Audio file already exists, removing it from ${finalDestinationFile.absolutePath}")
Log.d(
tag,
"Audio file already exists, removing it from ${finalDestinationFile.absolutePath}"
)
finalDestinationFile.delete()
}
val downloadItemPart = DownloadItemPart.make(downloadItem.id, destinationFilename, fileSize, destinationFile,finalDestinationFile,itemSubfolder,serverPath,localFolder,null,audioTrack,null)
val downloadItemPart =
DownloadItemPart.make(
downloadItem.id,
destinationFilename,
fileSize,
destinationFile,
finalDestinationFile,
itemSubfolder,
serverPath,
localFolder,
null,
audioTrack,
null
)
downloadItem.downloadItemParts.add(downloadItemPart)
}
if (downloadItem.downloadItemParts.isNotEmpty()) {
// Add cover download item
if (libraryItem.media.coverPath != null && libraryItem.media.coverPath?.isNotEmpty() == true) {
val coverLibraryFile = libraryItem.libraryFiles?.find { it.metadata.path == libraryItem.media.coverPath }
if (libraryItem.media.coverPath != null && libraryItem.media.coverPath?.isNotEmpty() == true
) {
val coverLibraryFile =
libraryItem.libraryFiles?.find { it.metadata.path == libraryItem.media.coverPath }
val coverFileSize = coverLibraryFile?.metadata?.size ?: 0
val serverPath = "/api/items/${libraryItem.id}/cover"
@@ -209,16 +298,35 @@ class AbsDownloader : Plugin() {
val finalDestinationFile = File("$itemFolderPath/$destinationFilename")
if (destinationFile.exists()) {
Log.d(tag, "TEMP Audio file already exists, removing it from ${destinationFile.absolutePath}")
Log.d(
tag,
"TEMP Audio file already exists, removing it from ${destinationFile.absolutePath}"
)
destinationFile.delete()
}
if (finalDestinationFile.exists()) {
Log.d(tag, "Cover already exists, removing it from ${finalDestinationFile.absolutePath}")
Log.d(
tag,
"Cover already exists, removing it from ${finalDestinationFile.absolutePath}"
)
finalDestinationFile.delete()
}
val downloadItemPart = DownloadItemPart.make(downloadItem.id, destinationFilename, coverFileSize, destinationFile,finalDestinationFile,itemSubfolder,serverPath,localFolder,null,null,null)
val downloadItemPart =
DownloadItemPart.make(
downloadItem.id,
destinationFilename,
coverFileSize,
destinationFile,
finalDestinationFile,
itemSubfolder,
serverPath,
localFolder,
null,
null,
null
)
downloadItem.downloadItemParts.add(downloadItemPart)
}
@@ -233,26 +341,64 @@ class AbsDownloader : Plugin() {
val fileSize = audioTrack?.metadata?.size ?: 0
Log.d(tag, "Starting podcast episode download")
val itemFolderPath = if (isInternal) "$tempFolderPath" else "${localFolder.absolutePath}/$podcastTitle"
val itemFolderPath =
if (isInternal) "$tempFolderPath" else "${localFolder.absolutePath}/$podcastTitle"
val downloadItemId = "${libraryItem.id}-${episode?.id}"
val downloadItem = DownloadItem(downloadItemId, libraryItem.id, episode?.id, libraryItem.userMediaProgress, DeviceManager.serverConnectionConfig?.id ?: "", DeviceManager.serverAddress, DeviceManager.serverUserId, libraryItem.mediaType, itemFolderPath, localFolder, podcastTitle, podcastTitle, libraryItem.media, mutableListOf())
val downloadItem =
DownloadItem(
downloadItemId,
libraryItem.id,
episode?.id,
libraryItem.userMediaProgress,
DeviceManager.serverConnectionConfig?.id ?: "",
DeviceManager.serverAddress,
DeviceManager.serverUserId,
libraryItem.mediaType,
itemFolderPath,
localFolder,
podcastTitle,
podcastTitle,
libraryItem.media,
mutableListOf()
)
var serverPath = "/api/items/${libraryItem.id}/file/${audioFileIno}/download"
var destinationFilename = getFilenameFromRelPath(audioTrack?.relPath ?: "")
Log.d(tag, "Audio File Server Path $serverPath | AF RelPath ${audioTrack?.relPath} | LocalFolder Path ${localFolder.absolutePath} | DestName $destinationFilename")
Log.d(
tag,
"Audio File Server Path $serverPath | AF RelPath ${audioTrack?.relPath} | LocalFolder Path ${localFolder.absolutePath} | DestName $destinationFilename"
)
var destinationFile = File("$tempFolderPath/$destinationFilename")
var finalDestinationFile = File("$itemFolderPath/$destinationFilename")
if (finalDestinationFile.exists()) {
Log.d(tag, "Audio file already exists, removing it from ${finalDestinationFile.absolutePath}")
Log.d(
tag,
"Audio file already exists, removing it from ${finalDestinationFile.absolutePath}"
)
finalDestinationFile.delete()
}
var downloadItemPart = DownloadItemPart.make(downloadItem.id, destinationFilename,fileSize, destinationFile,finalDestinationFile,podcastTitle,serverPath,localFolder,null,audioTrack,episode)
var downloadItemPart =
DownloadItemPart.make(
downloadItem.id,
destinationFilename,
fileSize,
destinationFile,
finalDestinationFile,
podcastTitle,
serverPath,
localFolder,
null,
audioTrack,
episode
)
downloadItem.downloadItemParts.add(downloadItemPart)
if (libraryItem.media.coverPath != null && libraryItem.media.coverPath?.isNotEmpty() == true) {
val coverLibraryFile = libraryItem.libraryFiles?.find { it.metadata.path == libraryItem.media.coverPath }
if (libraryItem.media.coverPath != null && libraryItem.media.coverPath?.isNotEmpty() == true
) {
val coverLibraryFile =
libraryItem.libraryFiles?.find { it.metadata.path == libraryItem.media.coverPath }
val coverFileSize = coverLibraryFile?.metadata?.size ?: 0
serverPath = "/api/items/${libraryItem.id}/cover"
@@ -264,7 +410,20 @@ class AbsDownloader : Plugin() {
if (finalDestinationFile.exists()) {
Log.d(tag, "Podcast cover already exists - not downloading cover again")
} else {
downloadItemPart = DownloadItemPart.make(downloadItem.id, destinationFilename,coverFileSize,destinationFile,finalDestinationFile,podcastTitle,serverPath,localFolder,null,null,null)
downloadItemPart =
DownloadItemPart.make(
downloadItem.id,
destinationFilename,
coverFileSize,
destinationFile,
finalDestinationFile,
podcastTitle,
serverPath,
localFolder,
null,
null,
null
)
downloadItem.downloadItemParts.add(downloadItemPart)
}
}