mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-29 14:47:21 +02:00
Android fix downloader to first download to temp directory then move files to final destination folder
This commit is contained in:
@@ -251,7 +251,7 @@ class FolderScanner(var ctx: Context) {
|
|||||||
Log.d(tag, "scanDownloadItem starting for ${downloadItem.itemFolderPath} | ${df.uri} | Item Folder Id:$itemFolderId | LLI Id:$localLibraryItemId")
|
Log.d(tag, "scanDownloadItem starting for ${downloadItem.itemFolderPath} | ${df.uri} | Item Folder Id:$itemFolderId | LLI Id:$localLibraryItemId")
|
||||||
|
|
||||||
// Search for files in media item folder
|
// Search for files in media item folder
|
||||||
var filesFound = df.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*"))
|
var filesFound = df.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*", "video/mp4"))
|
||||||
Log.d(tag, "scanDownloadItem ${filesFound.size} files found in ${downloadItem.itemFolderPath}")
|
Log.d(tag, "scanDownloadItem ${filesFound.size} files found in ${downloadItem.itemFolderPath}")
|
||||||
|
|
||||||
var localLibraryItem:LocalLibraryItem? = null
|
var localLibraryItem:LocalLibraryItem? = null
|
||||||
@@ -349,7 +349,7 @@ class FolderScanner(var ctx: Context) {
|
|||||||
var wasUpdated = false
|
var wasUpdated = false
|
||||||
|
|
||||||
// Search for files in media item folder
|
// Search for files in media item folder
|
||||||
var filesFound = df.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*"))
|
var filesFound = df.search(false, DocumentFileType.FILE, arrayOf("audio/*", "image/*", "video/mp4"))
|
||||||
Log.d(tag, "scanLocalLibraryItem ${filesFound.size} files found in ${localLibraryItem.absolutePath}")
|
Log.d(tag, "scanLocalLibraryItem ${filesFound.size} files found in ${localLibraryItem.absolutePath}")
|
||||||
|
|
||||||
filesFound.forEach {
|
filesFound.forEach {
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ import android.app.DownloadManager
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Environment
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
import com.anggrayudi.storage.callback.FileCallback
|
||||||
|
import com.anggrayudi.storage.file.*
|
||||||
|
import com.anggrayudi.storage.media.FileDescription
|
||||||
import com.audiobookshelf.app.MainActivity
|
import com.audiobookshelf.app.MainActivity
|
||||||
import com.audiobookshelf.app.data.*
|
import com.audiobookshelf.app.data.*
|
||||||
import com.audiobookshelf.app.device.DeviceManager
|
import com.audiobookshelf.app.device.DeviceManager
|
||||||
@@ -37,19 +42,51 @@ class AbsDownloader : Plugin() {
|
|||||||
data class DownloadItemPart(
|
data class DownloadItemPart(
|
||||||
val id: String,
|
val id: String,
|
||||||
val filename: String,
|
val filename: String,
|
||||||
val destinationPath:String,
|
val finalDestinationPath:String,
|
||||||
val itemTitle: String,
|
val itemTitle: String,
|
||||||
val serverPath: String,
|
val serverPath: String,
|
||||||
val localFolderName: String,
|
val localFolderName: String,
|
||||||
|
val localFolderUrl: String,
|
||||||
val localFolderId: String,
|
val localFolderId: String,
|
||||||
val audioTrack: AudioTrack?,
|
val audioTrack: AudioTrack?,
|
||||||
val episode:PodcastEpisode?,
|
val episode:PodcastEpisode?,
|
||||||
var completed:Boolean,
|
var completed:Boolean,
|
||||||
|
var moved:Boolean,
|
||||||
|
var failed:Boolean,
|
||||||
@JsonIgnore val uri: Uri,
|
@JsonIgnore val uri: Uri,
|
||||||
@JsonIgnore val destinationUri: Uri,
|
@JsonIgnore val destinationUri: Uri,
|
||||||
|
@JsonIgnore val finalDestinationUri: Uri,
|
||||||
var downloadId: Long?,
|
var downloadId: Long?,
|
||||||
var progress: Long
|
var progress: Long
|
||||||
) {
|
) {
|
||||||
|
companion object {
|
||||||
|
fun make(filename:String, destinationFile:File, finalDestinationFile:File, itemTitle:String, serverPath:String, localFolder:LocalFolder, audioTrack:AudioTrack?, episode:PodcastEpisode?) :DownloadItemPart {
|
||||||
|
val destinationUri = Uri.fromFile(destinationFile)
|
||||||
|
val finalDestinationUri = Uri.fromFile(finalDestinationFile)
|
||||||
|
val downloadUri = Uri.parse("${DeviceManager.serverAddress}${serverPath}?token=${DeviceManager.token}")
|
||||||
|
Log.d("DownloadItemPart", "Audio File Destination Uri: $destinationUri | Final Destination Uri: $finalDestinationUri | Download URI $downloadUri")
|
||||||
|
return DownloadItemPart(
|
||||||
|
id = DeviceManager.getBase64Id(finalDestinationFile.absolutePath),
|
||||||
|
filename = filename, finalDestinationFile.absolutePath,
|
||||||
|
itemTitle = itemTitle,
|
||||||
|
serverPath = serverPath,
|
||||||
|
localFolderName = localFolder.name,
|
||||||
|
localFolderUrl = localFolder.contentUrl,
|
||||||
|
localFolderId = localFolder.id,
|
||||||
|
audioTrack = audioTrack,
|
||||||
|
episode = episode,
|
||||||
|
completed = false,
|
||||||
|
moved = false,
|
||||||
|
failed = false,
|
||||||
|
uri = downloadUri,
|
||||||
|
destinationUri = destinationUri,
|
||||||
|
finalDestinationUri = finalDestinationUri,
|
||||||
|
downloadId = null,
|
||||||
|
progress = 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getDownloadRequest(): DownloadManager.Request {
|
fun getDownloadRequest(): DownloadManager.Request {
|
||||||
val dlRequest = DownloadManager.Request(uri)
|
val dlRequest = DownloadManager.Request(uri)
|
||||||
@@ -92,10 +129,10 @@ class AbsDownloader : Plugin() {
|
|||||||
val libraryItemId = call.data.getString("libraryItemId").toString()
|
val libraryItemId = call.data.getString("libraryItemId").toString()
|
||||||
var episodeId = call.data.getString("episodeId").toString()
|
var episodeId = call.data.getString("episodeId").toString()
|
||||||
if (episodeId == "null") episodeId = ""
|
if (episodeId == "null") episodeId = ""
|
||||||
var localFolderId = call.data.getString("localFolderId").toString()
|
val 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")
|
||||||
|
|
||||||
var downloadId = if (episodeId.isNullOrEmpty()) libraryItemId else "$libraryItemId-$episodeId"
|
val downloadId = if (episodeId.isEmpty()) libraryItemId else "$libraryItemId-$episodeId"
|
||||||
if (downloadQueue.find { it.id == downloadId } != null) {
|
if (downloadQueue.find { it.id == downloadId } != null) {
|
||||||
Log.d(tag, "Download already started for this media entity $downloadId")
|
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\"}"))
|
||||||
@@ -104,15 +141,15 @@ class AbsDownloader : Plugin() {
|
|||||||
apiHandler.getLibraryItem(libraryItemId) { libraryItem ->
|
apiHandler.getLibraryItem(libraryItemId) { libraryItem ->
|
||||||
Log.d(tag, "Got library item from server ${libraryItem.id}")
|
Log.d(tag, "Got library item from server ${libraryItem.id}")
|
||||||
|
|
||||||
var localFolder = DeviceManager.dbManager.getLocalFolder(localFolderId)
|
val localFolder = DeviceManager.dbManager.getLocalFolder(localFolderId)
|
||||||
if (localFolder != null) {
|
if (localFolder != null) {
|
||||||
|
|
||||||
if (!episodeId.isNullOrEmpty() && libraryItem.mediaType != "podcast") {
|
if (episodeId.isNotEmpty() && libraryItem.mediaType != "podcast") {
|
||||||
Log.e(tag, "Library item is not a podcast but episode was requested")
|
Log.e(tag, "Library item is not a podcast but episode was requested")
|
||||||
call.resolve(JSObject("{\"error\":\"Invalid library item not a podcast\"}"))
|
call.resolve(JSObject("{\"error\":\"Invalid library item not a podcast\"}"))
|
||||||
} else if (!episodeId.isNullOrEmpty()) {
|
} else if (episodeId.isNotEmpty()) {
|
||||||
var podcast = libraryItem.media as Podcast
|
val podcast = libraryItem.media as Podcast
|
||||||
var episode = podcast.episodes?.find { podcastEpisode ->
|
val episode = podcast.episodes?.find { podcastEpisode ->
|
||||||
podcastEpisode.id == episodeId
|
podcastEpisode.id == episodeId
|
||||||
}
|
}
|
||||||
if (episode == null) {
|
if (episode == null) {
|
||||||
@@ -132,77 +169,70 @@ class AbsDownloader : Plugin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clean folder path so it can be used in URL
|
// Clean folder path so it can be used in URL
|
||||||
fun cleanRelPath(relPath: String): String {
|
private fun cleanRelPath(relPath: String): String {
|
||||||
var cleanedRelPath = relPath.replace("\\", "/").replace("%", "%25").replace("#", "%23")
|
val cleanedRelPath = relPath.replace("\\", "/").replace("%", "%25").replace("#", "%23")
|
||||||
return if (cleanedRelPath.startsWith("/")) cleanedRelPath.substring(1) else cleanedRelPath
|
return if (cleanedRelPath.startsWith("/")) cleanedRelPath.substring(1) else cleanedRelPath
|
||||||
}
|
}
|
||||||
|
|
||||||
// Item filenames could be the same if they are in subfolders, this will make them unique
|
// Item filenames could be the same if they are in sub-folders, this will make them unique
|
||||||
fun getFilenameFromRelPath(relPath: String): String {
|
private fun getFilenameFromRelPath(relPath: String): String {
|
||||||
var cleanedRelPath = relPath.replace("\\", "_").replace("/", "_")
|
val cleanedRelPath = relPath.replace("\\", "_").replace("/", "_")
|
||||||
return if (cleanedRelPath.startsWith("_")) cleanedRelPath.substring(1) else cleanedRelPath
|
return if (cleanedRelPath.startsWith("_")) cleanedRelPath.substring(1) else cleanedRelPath
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startLibraryItemDownload(libraryItem: LibraryItem, localFolder: LocalFolder, episode:PodcastEpisode?) {
|
private fun startLibraryItemDownload(libraryItem: LibraryItem, localFolder: LocalFolder, episode:PodcastEpisode?) {
|
||||||
|
val tempFolderPath = mainActivity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
|
||||||
|
|
||||||
if (libraryItem.mediaType == "book") {
|
if (libraryItem.mediaType == "book") {
|
||||||
var bookTitle = libraryItem.media.metadata.title
|
val bookTitle = libraryItem.media.metadata.title
|
||||||
var tracks = libraryItem.media.getAudioTracks()
|
val tracks = libraryItem.media.getAudioTracks()
|
||||||
Log.d(tag, "Starting library item download with ${tracks.size} tracks")
|
Log.d(tag, "Starting library item download with ${tracks.size} tracks")
|
||||||
var itemFolderPath = localFolder.absolutePath + "/" + bookTitle
|
val itemFolderPath = localFolder.absolutePath + "/" + bookTitle
|
||||||
var downloadItem = DownloadItem(libraryItem.id, libraryItem.id, null,DeviceManager.serverConnectionConfig?.id ?: "", DeviceManager.serverAddress, DeviceManager.serverUserId, libraryItem.mediaType, itemFolderPath, localFolder, bookTitle, libraryItem.media, mutableListOf())
|
val downloadItem = DownloadItem(libraryItem.id, libraryItem.id, null,DeviceManager.serverConnectionConfig?.id ?: "", DeviceManager.serverAddress, DeviceManager.serverUserId, libraryItem.mediaType, itemFolderPath, localFolder, bookTitle, libraryItem.media, mutableListOf())
|
||||||
|
|
||||||
// Create download item part for each audio track
|
// Create download item part for each audio track
|
||||||
tracks.forEach { audioTrack ->
|
tracks.forEach { audioTrack ->
|
||||||
var serverPath = "/s/item/${libraryItem.id}/${cleanRelPath(audioTrack.relPath)}"
|
val serverPath = "/s/item/${libraryItem.id}/${cleanRelPath(audioTrack.relPath)}"
|
||||||
var destinationFilename = getFilenameFromRelPath(audioTrack.relPath)
|
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}")
|
||||||
var destinationFile = File("$itemFolderPath/$destinationFilename")
|
|
||||||
|
|
||||||
if (destinationFile.exists()) {
|
val finalDestinationFile = File("$itemFolderPath/$destinationFilename")
|
||||||
Log.d(tag, "Audio file already exists, removing it from ${destinationFile.absolutePath}")
|
val destinationFile = File("$tempFolderPath/$destinationFilename")
|
||||||
destinationFile.delete()
|
|
||||||
|
if (finalDestinationFile.exists()) {
|
||||||
|
Log.d(tag, "Audio file already exists, removing it from ${finalDestinationFile.absolutePath}")
|
||||||
|
finalDestinationFile.delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
var destinationUri = Uri.fromFile(destinationFile)
|
val downloadItemPart = DownloadItemPart.make(destinationFilename,destinationFile,finalDestinationFile,bookTitle,serverPath,localFolder,audioTrack,null)
|
||||||
var downloadUri = Uri.parse("${DeviceManager.serverAddress}${serverPath}?token=${DeviceManager.token}")
|
|
||||||
Log.d(tag, "Audio File Destination Uri $destinationUri | Download URI $downloadUri")
|
|
||||||
var downloadItemPart = DownloadItemPart(DeviceManager.getBase64Id(destinationFile.absolutePath), destinationFilename, destinationFile.absolutePath, bookTitle, serverPath, localFolder.name, localFolder.id, audioTrack, null, false, downloadUri, destinationUri, null, 0)
|
|
||||||
|
|
||||||
downloadItem.downloadItemParts.add(downloadItemPart)
|
downloadItem.downloadItemParts.add(downloadItemPart)
|
||||||
|
|
||||||
var dlRequest = downloadItemPart.getDownloadRequest()
|
val dlRequest = downloadItemPart.getDownloadRequest()
|
||||||
var downloadId = downloadManager.enqueue(dlRequest)
|
val downloadId = downloadManager.enqueue(dlRequest)
|
||||||
downloadItemPart.downloadId = downloadId
|
downloadItemPart.downloadId = downloadId
|
||||||
}
|
}
|
||||||
|
|
||||||
if (downloadItem.downloadItemParts.isNotEmpty()) {
|
if (downloadItem.downloadItemParts.isNotEmpty()) {
|
||||||
// Add cover download item
|
// Add cover download item
|
||||||
if (libraryItem.media.coverPath != null && libraryItem.media.coverPath?.isNotEmpty() == true) {
|
if (libraryItem.media.coverPath != null && libraryItem.media.coverPath?.isNotEmpty() == true) {
|
||||||
var serverPath = "/api/items/${libraryItem.id}/cover?format=jpeg"
|
val serverPath = "/api/items/${libraryItem.id}/cover?format=jpeg"
|
||||||
var destinationFilename = "cover.jpg"
|
val destinationFilename = "cover.jpg"
|
||||||
var destinationFile = File("$itemFolderPath/$destinationFilename")
|
val destinationFile = File("$tempFolderPath/$destinationFilename")
|
||||||
|
val finalDestinationFile = File("$itemFolderPath/$destinationFilename")
|
||||||
|
|
||||||
if (destinationFile.exists()) {
|
if (finalDestinationFile.exists()) {
|
||||||
Log.d(tag, "Cover already exists, removing it from ${destinationFile.absolutePath}")
|
Log.d(tag, "Cover already exists, removing it from ${finalDestinationFile.absolutePath}")
|
||||||
destinationFile.delete()
|
finalDestinationFile.delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
var destinationUri = Uri.fromFile(destinationFile)
|
val downloadItemPart = DownloadItemPart.make(destinationFilename,destinationFile,finalDestinationFile,bookTitle,serverPath,localFolder,null,null)
|
||||||
var downloadUri = Uri.parse("${DeviceManager.serverAddress}${serverPath}&token=${DeviceManager.token}")
|
|
||||||
var downloadItemPart = DownloadItemPart(DeviceManager.getBase64Id(destinationFile.absolutePath), destinationFilename, destinationFile.absolutePath, bookTitle, serverPath, localFolder.name, localFolder.id, null,null, false, downloadUri, destinationUri, null, 0)
|
|
||||||
|
|
||||||
downloadItem.downloadItemParts.add(downloadItemPart)
|
downloadItem.downloadItemParts.add(downloadItemPart)
|
||||||
|
|
||||||
var dlRequest = downloadItemPart.getDownloadRequest()
|
val dlRequest = downloadItemPart.getDownloadRequest()
|
||||||
var downloadId = downloadManager.enqueue(dlRequest)
|
val downloadId = downloadManager.enqueue(dlRequest)
|
||||||
downloadItemPart.downloadId = downloadId
|
downloadItemPart.downloadId = downloadId
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Cannot create new text file here but can download here... ??
|
|
||||||
// var abmetadataFile = File(itemFolderPath, "abmetadata.abs")
|
|
||||||
// abmetadataFile.createNewFileIfPossible()
|
|
||||||
// abmetadataFile.writeText(getAbMetadataText(libraryItem))
|
|
||||||
|
|
||||||
downloadQueue.add(downloadItem)
|
downloadQueue.add(downloadItem)
|
||||||
startWatchingDownloads(downloadItem)
|
startWatchingDownloads(downloadItem)
|
||||||
DeviceManager.dbManager.saveDownloadItem(downloadItem)
|
DeviceManager.dbManager.saveDownloadItem(downloadItem)
|
||||||
@@ -210,26 +240,25 @@ class AbsDownloader : Plugin() {
|
|||||||
} else {
|
} else {
|
||||||
// Podcast episode download
|
// Podcast episode download
|
||||||
|
|
||||||
var podcastTitle = libraryItem.media.metadata.title
|
val podcastTitle = libraryItem.media.metadata.title
|
||||||
var audioTrack = episode?.audioTrack
|
val audioTrack = episode?.audioTrack
|
||||||
Log.d(tag, "Starting podcast episode download")
|
Log.d(tag, "Starting podcast episode download")
|
||||||
var itemFolderPath = localFolder.absolutePath + "/" + podcastTitle
|
val itemFolderPath = localFolder.absolutePath + "/" + podcastTitle
|
||||||
var downloadItemId = "${libraryItem.id}-${episode?.id}"
|
val downloadItemId = "${libraryItem.id}-${episode?.id}"
|
||||||
var downloadItem = DownloadItem(downloadItemId, libraryItem.id, episode?.id, DeviceManager.serverConnectionConfig?.id ?: "", DeviceManager.serverAddress, DeviceManager.serverUserId, libraryItem.mediaType, itemFolderPath, localFolder, podcastTitle, libraryItem.media, mutableListOf())
|
val downloadItem = DownloadItem(downloadItemId, libraryItem.id, episode?.id, DeviceManager.serverConnectionConfig?.id ?: "", DeviceManager.serverAddress, DeviceManager.serverUserId, libraryItem.mediaType, itemFolderPath, localFolder, podcastTitle, libraryItem.media, mutableListOf())
|
||||||
|
|
||||||
var serverPath = "/s/item/${libraryItem.id}/${cleanRelPath(audioTrack?.relPath ?: "")}"
|
var serverPath = "/s/item/${libraryItem.id}/${cleanRelPath(audioTrack?.relPath ?: "")}"
|
||||||
var destinationFilename = getFilenameFromRelPath(audioTrack?.relPath ?: "")
|
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("$itemFolderPath/$destinationFilename")
|
|
||||||
if (destinationFile.exists()) {
|
var destinationFile = File("$tempFolderPath/$destinationFilename")
|
||||||
Log.d(tag, "Audio file already exists, removing it from ${destinationFile.absolutePath}")
|
var finalDestinationFile = File("$itemFolderPath/$destinationFilename")
|
||||||
destinationFile.delete()
|
if (finalDestinationFile.exists()) {
|
||||||
|
Log.d(tag, "Audio file already exists, removing it from ${finalDestinationFile.absolutePath}")
|
||||||
|
finalDestinationFile.delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
var destinationUri = Uri.fromFile(destinationFile)
|
var downloadItemPart = DownloadItemPart.make(destinationFilename,destinationFile,finalDestinationFile,podcastTitle,serverPath,localFolder,audioTrack,null)
|
||||||
var downloadUri = Uri.parse("${DeviceManager.serverAddress}${serverPath}?token=${DeviceManager.token}")
|
|
||||||
Log.d(tag, "Audio File Destination Uri $destinationUri | Download URI $downloadUri")
|
|
||||||
var downloadItemPart = DownloadItemPart(DeviceManager.getBase64Id(destinationFile.absolutePath), destinationFilename, destinationFile.absolutePath, podcastTitle, serverPath, localFolder.name, localFolder.id, audioTrack, episode,false, downloadUri, destinationUri, null, 0)
|
|
||||||
downloadItem.downloadItemParts.add(downloadItemPart)
|
downloadItem.downloadItemParts.add(downloadItemPart)
|
||||||
|
|
||||||
var dlRequest = downloadItemPart.getDownloadRequest()
|
var dlRequest = downloadItemPart.getDownloadRequest()
|
||||||
@@ -239,15 +268,14 @@ class AbsDownloader : Plugin() {
|
|||||||
if (libraryItem.media.coverPath != null && libraryItem.media.coverPath?.isNotEmpty() == true) {
|
if (libraryItem.media.coverPath != null && libraryItem.media.coverPath?.isNotEmpty() == true) {
|
||||||
serverPath = "/api/items/${libraryItem.id}/cover?format=jpeg"
|
serverPath = "/api/items/${libraryItem.id}/cover?format=jpeg"
|
||||||
destinationFilename = "cover.jpg"
|
destinationFilename = "cover.jpg"
|
||||||
destinationFile = File("$itemFolderPath/$destinationFilename")
|
|
||||||
|
|
||||||
if (destinationFile.exists()) {
|
destinationFile = File("$tempFolderPath/$destinationFilename")
|
||||||
|
finalDestinationFile = File("$itemFolderPath/$destinationFilename")
|
||||||
|
|
||||||
|
if (finalDestinationFile.exists()) {
|
||||||
Log.d(tag, "Podcast cover already exists - not downloading cover again")
|
Log.d(tag, "Podcast cover already exists - not downloading cover again")
|
||||||
} else {
|
} else {
|
||||||
destinationUri = Uri.fromFile(destinationFile)
|
downloadItemPart = DownloadItemPart.make(destinationFilename,destinationFile,finalDestinationFile,podcastTitle,serverPath,localFolder,audioTrack,null)
|
||||||
downloadUri = Uri.parse("${DeviceManager.serverAddress}${serverPath}&token=${DeviceManager.token}")
|
|
||||||
downloadItemPart = DownloadItemPart(DeviceManager.getBase64Id(destinationFile.absolutePath), destinationFilename, destinationFile.absolutePath, podcastTitle, serverPath, localFolder.name, localFolder.id, null,null, false, downloadUri, destinationUri, null, 0)
|
|
||||||
|
|
||||||
downloadItem.downloadItemParts.add(downloadItemPart)
|
downloadItem.downloadItemParts.add(downloadItemPart)
|
||||||
|
|
||||||
dlRequest = downloadItemPart.getDownloadRequest()
|
dlRequest = downloadItemPart.getDownloadRequest()
|
||||||
@@ -264,8 +292,8 @@ class AbsDownloader : Plugin() {
|
|||||||
|
|
||||||
fun startWatchingDownloads(downloadItem: DownloadItem) {
|
fun startWatchingDownloads(downloadItem: DownloadItem) {
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
while (downloadItem.downloadItemParts.find { !it.completed } != null) { // While some item is not completed
|
while (downloadItem.downloadItemParts.find { !it.moved && !it.failed } != null) { // While some item is not completed
|
||||||
var numPartsBefore = downloadItem.downloadItemParts.size
|
val numPartsBefore = downloadItem.downloadItemParts.size
|
||||||
checkDownloads(downloadItem)
|
checkDownloads(downloadItem)
|
||||||
|
|
||||||
// Keep database updated as item parts finish downloading
|
// Keep database updated as item parts finish downloading
|
||||||
@@ -278,13 +306,13 @@ class AbsDownloader : Plugin() {
|
|||||||
delay(500)
|
delay(500)
|
||||||
}
|
}
|
||||||
|
|
||||||
var localLibraryItem = folderScanner.scanDownloadItem(downloadItem)
|
val localLibraryItem = folderScanner.scanDownloadItem(downloadItem)
|
||||||
DeviceManager.dbManager.removeDownloadItem(downloadItem.id)
|
DeviceManager.dbManager.removeDownloadItem(downloadItem.id)
|
||||||
downloadQueue.remove(downloadItem)
|
downloadQueue.remove(downloadItem)
|
||||||
|
|
||||||
Log.d(tag, "Item download complete ${downloadItem.itemTitle} | local library item id: ${localLibraryItem?.id} | Items remaining in Queue ${downloadQueue.size}")
|
Log.d(tag, "Item download complete ${downloadItem.itemTitle} | local library item id: ${localLibraryItem?.id} | Items remaining in Queue ${downloadQueue.size}")
|
||||||
|
|
||||||
var jsobj = JSObject()
|
val jsobj = JSObject()
|
||||||
jsobj.put("libraryItemId", downloadItem.id)
|
jsobj.put("libraryItemId", downloadItem.id)
|
||||||
jsobj.put("localFolderId", downloadItem.localFolder.id)
|
jsobj.put("localFolderId", downloadItem.localFolder.id)
|
||||||
if (localLibraryItem != null) {
|
if (localLibraryItem != null) {
|
||||||
@@ -295,26 +323,58 @@ class AbsDownloader : Plugin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun checkDownloads(downloadItem: DownloadItem) {
|
fun checkDownloads(downloadItem: DownloadItem) {
|
||||||
var itemParts = downloadItem.downloadItemParts.map { it }
|
val itemParts = downloadItem.downloadItemParts.map { it }
|
||||||
for (downloadItemPart in itemParts) {
|
for (downloadItemPart in itemParts) {
|
||||||
if (downloadItemPart.downloadId != null) {
|
if (downloadItemPart.downloadId != null) {
|
||||||
var dlid = downloadItemPart.downloadId!!
|
val dlid = downloadItemPart.downloadId!!
|
||||||
val query = DownloadManager.Query().setFilterById(dlid)
|
val query = DownloadManager.Query().setFilterById(dlid)
|
||||||
downloadManager.query(query).use {
|
downloadManager.query(query).use {
|
||||||
if (it.moveToFirst()) {
|
if (it.moveToFirst()) {
|
||||||
val totalBytes = it.getInt(it.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
|
val bytesColumnIndex = it.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)
|
||||||
val downloadStatus = it.getInt(it.getColumnIndex(DownloadManager.COLUMN_STATUS))
|
val statusColumnIndex = it.getColumnIndex(DownloadManager.COLUMN_STATUS)
|
||||||
val bytesDownloadedSoFar = it.getInt(it.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
|
val bytesDownloadedColumnIndex = it.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)
|
||||||
|
|
||||||
|
val totalBytes = if (bytesColumnIndex >= 0) it.getInt(bytesColumnIndex) else 0
|
||||||
|
val downloadStatus = if (statusColumnIndex >= 0) it.getInt(statusColumnIndex) else 0
|
||||||
|
val bytesDownloadedSoFar = if (bytesDownloadedColumnIndex >= 0) it.getInt(bytesDownloadedColumnIndex) else 0
|
||||||
Log.d(tag, "checkDownloads Download ${downloadItemPart.filename} bytes $totalBytes | bytes dled $bytesDownloadedSoFar | downloadStatus $downloadStatus")
|
Log.d(tag, "checkDownloads Download ${downloadItemPart.filename} bytes $totalBytes | bytes dled $bytesDownloadedSoFar | downloadStatus $downloadStatus")
|
||||||
|
|
||||||
if (downloadStatus == DownloadManager.STATUS_SUCCESSFUL) {
|
if (downloadStatus == DownloadManager.STATUS_SUCCESSFUL) {
|
||||||
Log.d(tag, "checkDownloads Download ${downloadItemPart.filename} Done")
|
// Once file download is complete move the file to the final destination
|
||||||
// downloadItem.downloadItemParts.remove(downloadItemPart)
|
if (!downloadItemPart.completed) {
|
||||||
downloadItemPart.completed = true
|
Log.d(tag, "checkDownloads Download ${downloadItemPart.filename} Done")
|
||||||
|
downloadItemPart.completed = true
|
||||||
|
val file = DocumentFileCompat.fromUri(mainActivity, downloadItemPart.destinationUri)
|
||||||
|
Log.d(tag, "DOWNLOAD: Attempt move for file at destination ${downloadItemPart.destinationUri} | ${file?.getBasePath(mainActivity)}")
|
||||||
|
|
||||||
|
val fcb = object : FileCallback() {
|
||||||
|
override fun onPrepare() {
|
||||||
|
Log.d(tag, "DOWNLOAD: PREPARING MOVE FILE")
|
||||||
|
}
|
||||||
|
override fun onFailed(errorCode:ErrorCode) {
|
||||||
|
Log.e(tag, "DOWNLOAD: FAILED TO MOVE FILE $errorCode")
|
||||||
|
downloadItemPart.failed = true
|
||||||
|
file?.delete()
|
||||||
|
}
|
||||||
|
override fun onCompleted(result:Any) {
|
||||||
|
Log.d(tag, "DOWNLOAD: FILE MOVE COMPLETED")
|
||||||
|
val resultDocFile = result as DocumentFile
|
||||||
|
Log.d(tag, "DOWNLOAD: COMPLETED FILE INFO ${resultDocFile.getAbsolutePath(mainActivity)}")
|
||||||
|
downloadItemPart.moved = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(tag, "DOWNLOAD: Move file to final destination path: ${downloadItemPart.finalDestinationPath}")
|
||||||
|
val localFolderFile = DocumentFileCompat.fromUri(mainActivity,Uri.parse(downloadItemPart.localFolderUrl))
|
||||||
|
val mimetype = if (downloadItemPart.audioTrack != null) MimeType.AUDIO else MimeType.IMAGE
|
||||||
|
val fileDescription = FileDescription(downloadItemPart.filename, downloadItemPart.itemTitle, mimetype)
|
||||||
|
file?.moveFileTo(mainActivity,localFolderFile!!,fileDescription,fcb)
|
||||||
|
} else {
|
||||||
|
// Why is kotlin requiring an else here..
|
||||||
|
}
|
||||||
} else if (downloadStatus == DownloadManager.STATUS_FAILED) {
|
} else if (downloadStatus == DownloadManager.STATUS_FAILED) {
|
||||||
Log.d(tag, "checkDownloads Download ${downloadItemPart.filename} Failed")
|
Log.d(tag, "checkDownloads Download ${downloadItemPart.filename} Failed")
|
||||||
downloadItem.downloadItemParts.remove(downloadItemPart)
|
downloadItem.downloadItemParts.remove(downloadItemPart)
|
||||||
// downloadItemPart.completed = true
|
|
||||||
} else {
|
} else {
|
||||||
//update progress
|
//update progress
|
||||||
val percentProgress = if (totalBytes > 0) ((bytesDownloadedSoFar * 100L) / totalBytes) else 0
|
val percentProgress = if (totalBytes > 0) ((bytesDownloadedSoFar * 100L) / totalBytes) else 0
|
||||||
|
|||||||
Reference in New Issue
Block a user