From 4a09238a8e13409e4052b4bfb0c42843b46d333d Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Fri, 28 Feb 2025 22:07:24 -0700 Subject: [PATCH] Refactor `AbsDownloader` --- .../app/plugins/AbsDownloader.kt | 293 ++++++------------ 1 file changed, 87 insertions(+), 206 deletions(-) diff --git a/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsDownloader.kt b/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsDownloader.kt index 758b5b50..1a035834 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsDownloader.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/plugins/AbsDownloader.kt @@ -160,66 +160,53 @@ class AbsDownloader : Plugin() { val tempFolderPath = if (isInternal) "${mainActivity.filesDir}/downloads/${libraryItem.id}" - else mainActivity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) + else "${mainActivity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)}" Log.d(tag, "downloadCacheDirectory=$tempFolderPath") + val itemFolderPath = + if (isInternal) "$tempFolderPath" + else + "${localFolder.absolutePath}/${cleanStringForFileSystem(libraryItem.media.metadata.title)}" + + val downloadItemId = if (episode != null) "${libraryItem.id}-${episode.id}" else libraryItem.id + val titleCleaned = cleanStringForFileSystem(libraryItem.media.metadata.title) + val author = + if (episode == null) + cleanStringForFileSystem(libraryItem.media.metadata.getAuthorDisplayName()) + else titleCleaned + val itemSubfolder = if (episode == null) "$author/$titleCleaned" else "$titleCleaned" + val downloadItem = + DownloadItem( + downloadItemId, + libraryItem.id, + episode?.id, + libraryItem.userMediaProgress, + DeviceManager.serverConnectionConfig?.id ?: "", + DeviceManager.serverAddress, + DeviceManager.serverUserId, + libraryItem.mediaType, + itemFolderPath, + localFolder, + titleCleaned, + itemSubfolder, + libraryItem.media, + mutableListOf() + ) + if (libraryItem.mediaType == "book") { - val bookTitle = cleanStringForFileSystem(libraryItem.media.metadata.title) - val bookAuthor = cleanStringForFileSystem(libraryItem.media.metadata.getAuthorDisplayName()) - - 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 book = libraryItem.media as Book book.ebookFile?.let { ebookFile -> - val fileSize = ebookFile.metadata?.size ?: 0 val serverPath = "/api/items/${libraryItem.id}/file/${ebookFile.ino}/download" val destinationFilename = getFilenameFromRelPath(ebookFile.metadata?.relPath ?: "") - val finalDestinationFile = File("$itemFolderPath/$destinationFilename") val destinationFile = File("$tempFolderPath/$destinationFilename") - - if (destinationFile.exists()) { - 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}" - ) - finalDestinationFile.delete() - } - + val finalDestinationFile = File("$itemFolderPath/$destinationFilename") + checkAndDeleteFile(destinationFile, finalDestinationFile) val downloadItemPart = DownloadItemPart.make( downloadItem.id, destinationFilename, - fileSize, + ebookFile.metadata?.size ?: 0, destinationFile, finalDestinationFile, itemSubfolder, @@ -232,46 +219,20 @@ class AbsDownloader : Plugin() { downloadItem.downloadItemParts.add(downloadItemPart) } - // Create download item part for each audio track - val audioFiles = (libraryItem.media as Book).audioFiles ?: mutableListOf() - 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 + val audioFiles = book.audioFiles ?: mutableListOf() + libraryItem.media.getAudioTracks().forEach { 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" - ) - - 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}" - ) - destinationFile.delete() - } - - if (finalDestinationFile.exists()) { - Log.d( - tag, - "Audio file already exists, removing it from ${finalDestinationFile.absolutePath}" - ) - finalDestinationFile.delete() - } - + val finalDestinationFile = File("$itemFolderPath/$destinationFilename") + checkAndDeleteFile(destinationFile, finalDestinationFile) val downloadItemPart = DownloadItemPart.make( downloadItem.id, destinationFilename, - fileSize, + audioTrack.metadata?.size ?: 0, destinationFile, finalDestinationFile, itemSubfolder, @@ -283,110 +244,22 @@ class AbsDownloader : Plugin() { ) 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 } - val coverFileSize = coverLibraryFile?.metadata?.size ?: 0 - - val serverPath = "/api/items/${libraryItem.id}/cover" - val destinationFilename = "cover-${libraryItem.id}.jpg" - val destinationFile = File("$tempFolderPath/$destinationFilename") - val finalDestinationFile = File("$itemFolderPath/$destinationFilename") - - if (destinationFile.exists()) { - 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}" - ) - finalDestinationFile.delete() - } - - val downloadItemPart = - DownloadItemPart.make( - downloadItem.id, - destinationFilename, - coverFileSize, - destinationFile, - finalDestinationFile, - itemSubfolder, - serverPath, - localFolder, - null, - null, - null - ) - downloadItem.downloadItemParts.add(downloadItemPart) - } - - downloadItemManager.addDownloadItem(downloadItem) - } } else { - // Podcast episode download - val podcastTitle = cleanStringForFileSystem(libraryItem.media.metadata.title) - val audioTrack = episode?.audioTrack val audioFileIno = episode?.audioFile?.ino - val fileSize = audioTrack?.metadata?.size ?: 0 - - Log.d(tag, "Starting podcast episode download") - 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() - ) - - 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" - ) - - 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}" - ) - finalDestinationFile.delete() - } - - var downloadItemPart = + val serverPath = "/api/items/${libraryItem.id}/file/${audioFileIno}/download" + val destinationFilename = getFilenameFromRelPath(audioTrack?.relPath ?: "") + val destinationFile = File("$tempFolderPath/$destinationFilename") + val finalDestinationFile = File("$itemFolderPath/$destinationFilename") + checkAndDeleteFile(destinationFile, finalDestinationFile) + val downloadItemPart = DownloadItemPart.make( downloadItem.id, destinationFilename, - fileSize, + audioTrack?.metadata?.size ?: 0, destinationFile, finalDestinationFile, - podcastTitle, + titleCleaned, serverPath, localFolder, null, @@ -394,41 +267,49 @@ class AbsDownloader : Plugin() { 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 } - val coverFileSize = coverLibraryFile?.metadata?.size ?: 0 - - serverPath = "/api/items/${libraryItem.id}/cover" - destinationFilename = "cover.jpg" - - destinationFile = File("$tempFolderPath/$destinationFilename") - finalDestinationFile = File("$itemFolderPath/$destinationFilename") - - 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 - ) - 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 } + val serverPath = "/api/items/${libraryItem.id}/cover" + val destinationFilename = if (episode == null) "cover-${libraryItem.id}.jpg" else "cover.jpg" + val destinationFile = File("$tempFolderPath/$destinationFilename") + val finalDestinationFile = File("$itemFolderPath/$destinationFilename") + checkAndDeleteFile(destinationFile, finalDestinationFile) + val downloadItemPart = + DownloadItemPart.make( + downloadItem.id, + destinationFilename, + coverLibraryFile?.metadata?.size ?: 0, + destinationFile, + finalDestinationFile, + titleCleaned, + serverPath, + localFolder, + null, + null, + null + ) + downloadItem.downloadItemParts.add(downloadItemPart) + } + if (downloadItem.downloadItemParts.isNotEmpty()) { downloadItemManager.addDownloadItem(downloadItem) } } + + /* Delete files if they already exist */ + private fun checkAndDeleteFile(destinationFile: File, finalDestinationFile: File) { + + if (destinationFile.exists()) { + Log.d(tag, "TEMP file already exists, removing it from ${destinationFile.absolutePath}") + destinationFile.delete() + } + + if (finalDestinationFile.exists()) { + Log.d(tag, "File already exists, removing it from ${finalDestinationFile.absolutePath}") + finalDestinationFile.delete() + } + } }