mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-06 20:08:48 +02:00
Update:More accurate progress percentage using bytes, download 1 audio file at a time & currently downloading page #251 #360 #515 #274
This commit is contained in:
@@ -220,7 +220,7 @@ class FolderScanner(var ctx: Context) {
|
||||
}
|
||||
|
||||
// Scan item after download and create local library item
|
||||
fun scanDownloadItem(downloadItem: DownloadItem):DownloadItemScanResult? {
|
||||
fun scanDownloadItem(downloadItem: DownloadItem, cb: (DownloadItemScanResult?) -> Unit) {
|
||||
val folderDf = DocumentFileCompat.fromUri(ctx, Uri.parse(downloadItem.localFolder.contentUrl))
|
||||
val foldersFound = folderDf?.search(true, DocumentFileType.FOLDER) ?: mutableListOf()
|
||||
|
||||
@@ -241,13 +241,13 @@ class FolderScanner(var ctx: Context) {
|
||||
|
||||
if (itemFolderUrl == "") {
|
||||
Log.d(tag, "scanDownloadItem failed to find media folder")
|
||||
return null
|
||||
return cb(null)
|
||||
}
|
||||
val df: DocumentFile? = DocumentFileCompat.fromUri(ctx, Uri.parse(itemFolderUrl))
|
||||
|
||||
if (df == null) {
|
||||
Log.e(tag, "Folder Doc File Invalid ${downloadItem.itemFolderPath}")
|
||||
return null
|
||||
return cb(null)
|
||||
}
|
||||
|
||||
val localLibraryItemId = getLocalLibraryItemId(itemFolderId)
|
||||
@@ -283,7 +283,7 @@ class FolderScanner(var ctx: Context) {
|
||||
}
|
||||
} else if (itemPart.audioTrack != null) { // Is audio track
|
||||
val audioTrackFromServer = itemPart.audioTrack
|
||||
Log.d(tag, "scanDownloadItem: Audio Track from Server index = ${audioTrackFromServer?.index}")
|
||||
Log.d(tag, "scanDownloadItem: Audio Track from Server index = ${audioTrackFromServer.index}")
|
||||
|
||||
val localFileId = DeviceManager.getBase64Id(docFile.id)
|
||||
val localFile = LocalFile(localFileId,docFile.name,docFile.uri.toString(),docFile.getBasePath(ctx),docFile.getAbsolutePath(ctx),docFile.getSimplePath(ctx),docFile.mimeType,docFile.length())
|
||||
@@ -314,7 +314,7 @@ class FolderScanner(var ctx: Context) {
|
||||
|
||||
if (audioTracks.isEmpty()) {
|
||||
Log.d(tag, "scanDownloadItem did not find any audio tracks in folder for ${downloadItem.itemFolderPath}")
|
||||
return null
|
||||
return cb(null)
|
||||
}
|
||||
|
||||
// For books sort audio tracks then set
|
||||
@@ -364,7 +364,7 @@ class FolderScanner(var ctx: Context) {
|
||||
|
||||
DeviceManager.dbManager.saveLocalLibraryItem(localLibraryItem)
|
||||
|
||||
return downloadItemScanResult
|
||||
cb(downloadItemScanResult)
|
||||
}
|
||||
|
||||
fun scanLocalLibraryItem(localLibraryItem:LocalLibraryItem, forceAudioProbe:Boolean):LocalLibraryItemScanResult? {
|
||||
|
||||
@@ -25,7 +25,7 @@ import kotlinx.coroutines.launch
|
||||
|
||||
class DownloadItemManager(var downloadManager:DownloadManager, var folderScanner: FolderScanner, var mainActivity: MainActivity, var clientEventEmitter:DownloadEventEmitter) {
|
||||
val tag = "DownloadItemManager"
|
||||
private val maxSimultaneousDownloads = 5
|
||||
private val maxSimultaneousDownloads = 1
|
||||
private var jacksonMapper = jacksonObjectMapper().enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature())
|
||||
|
||||
enum class DownloadCheckStatus {
|
||||
@@ -98,11 +98,11 @@ class DownloadItemManager(var downloadManager:DownloadManager, var folderScanner
|
||||
handleDownloadItemPartCheck(downloadCheckStatus, downloadItemPart)
|
||||
}
|
||||
|
||||
delay(500)
|
||||
|
||||
if (currentDownloadItemParts.size < maxSimultaneousDownloads) {
|
||||
checkUpdateDownloadQueue()
|
||||
}
|
||||
|
||||
delay(500)
|
||||
}
|
||||
|
||||
Log.d(tag, "Finished watching downloads")
|
||||
@@ -122,12 +122,14 @@ class DownloadItemManager(var downloadManager:DownloadManager, var folderScanner
|
||||
|
||||
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
|
||||
val bytesDownloadedSoFar = if (bytesDownloadedColumnIndex >= 0) it.getLong(bytesDownloadedColumnIndex) else 0
|
||||
Log.d(tag, "checkDownloads Download ${downloadItemPart.filename} bytes $totalBytes | bytes dled $bytesDownloadedSoFar | downloadStatus $downloadStatus")
|
||||
|
||||
if (downloadStatus == DownloadManager.STATUS_SUCCESSFUL) {
|
||||
Log.d(tag, "checkDownloads Download ${downloadItemPart.filename} Successful")
|
||||
downloadItemPart.completed = true
|
||||
downloadItemPart.progress = 1
|
||||
downloadItemPart.bytesDownloaded = bytesDownloadedSoFar
|
||||
return DownloadCheckStatus.Successful
|
||||
} else if (downloadStatus == DownloadManager.STATUS_FAILED) {
|
||||
Log.d(tag, "checkDownloads Download ${downloadItemPart.filename} Failed")
|
||||
@@ -139,6 +141,7 @@ class DownloadItemManager(var downloadManager:DownloadManager, var folderScanner
|
||||
val percentProgress = if (totalBytes > 0) ((bytesDownloadedSoFar * 100L) / totalBytes) else 0
|
||||
Log.d(tag, "checkDownloads Download ${downloadItemPart.filename} Progress = $percentProgress%")
|
||||
downloadItemPart.progress = percentProgress
|
||||
downloadItemPart.bytesDownloaded = bytesDownloadedSoFar
|
||||
return DownloadCheckStatus.InProgress
|
||||
}
|
||||
} else {
|
||||
@@ -214,23 +217,24 @@ class DownloadItemManager(var downloadManager:DownloadManager, var folderScanner
|
||||
if (downloadItem.isDownloadFinished) {
|
||||
Log.i(tag, "Download Item finished ${downloadItem.media.metadata.title}")
|
||||
|
||||
val downloadItemScanResult = folderScanner.scanDownloadItem(downloadItem)
|
||||
Log.d(tag, "Item download complete ${downloadItem.itemTitle} | local library item id: ${downloadItemScanResult?.localLibraryItem?.id}")
|
||||
folderScanner.scanDownloadItem(downloadItem) { downloadItemScanResult ->
|
||||
Log.d(tag, "Item download complete ${downloadItem.itemTitle} | local library item id: ${downloadItemScanResult?.localLibraryItem?.id}")
|
||||
|
||||
val jsobj = JSObject()
|
||||
jsobj.put("libraryItemId", downloadItem.id)
|
||||
jsobj.put("localFolderId", downloadItem.localFolder.id)
|
||||
val jsobj = JSObject()
|
||||
jsobj.put("libraryItemId", downloadItem.id)
|
||||
jsobj.put("localFolderId", downloadItem.localFolder.id)
|
||||
|
||||
downloadItemScanResult?.localLibraryItem?.let { localLibraryItem ->
|
||||
jsobj.put("localLibraryItem", JSObject(jacksonMapper.writeValueAsString(localLibraryItem)))
|
||||
downloadItemScanResult?.localLibraryItem?.let { localLibraryItem ->
|
||||
jsobj.put("localLibraryItem", JSObject(jacksonMapper.writeValueAsString(localLibraryItem)))
|
||||
}
|
||||
downloadItemScanResult?.localMediaProgress?.let { localMediaProgress ->
|
||||
jsobj.put("localMediaProgress", JSObject(jacksonMapper.writeValueAsString(localMediaProgress)))
|
||||
}
|
||||
|
||||
clientEventEmitter.onDownloadItemComplete(jsobj)
|
||||
downloadItemQueue.remove(downloadItem)
|
||||
DeviceManager.dbManager.removeDownloadItem(downloadItem.id)
|
||||
}
|
||||
downloadItemScanResult?.localMediaProgress?.let { localMediaProgress ->
|
||||
jsobj.put("localMediaProgress", JSObject(jacksonMapper.writeValueAsString(localMediaProgress)))
|
||||
}
|
||||
|
||||
clientEventEmitter.onDownloadItemComplete(jsobj)
|
||||
downloadItemQueue.remove(downloadItem)
|
||||
DeviceManager.dbManager.removeDownloadItem(downloadItem.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ data class DownloadItemPart(
|
||||
val id: String,
|
||||
val downloadItemId: String,
|
||||
val filename: String,
|
||||
val fileSize: Long,
|
||||
val finalDestinationPath:String,
|
||||
val serverPath: String,
|
||||
val localFolderName: String,
|
||||
@@ -31,10 +32,11 @@ data class DownloadItemPart(
|
||||
@JsonIgnore val finalDestinationUri: Uri,
|
||||
val finalDestinationSubfolder: String,
|
||||
var downloadId: Long?,
|
||||
var progress: Long
|
||||
var progress: Long,
|
||||
var bytesDownloaded: Long
|
||||
) {
|
||||
companion object {
|
||||
fun make(downloadItemId:String, filename:String, destinationFile: File, finalDestinationFile: File, subfolder:String, serverPath:String, localFolder: LocalFolder, audioTrack: AudioTrack?, episode: PodcastEpisode?) :DownloadItemPart {
|
||||
fun make(downloadItemId:String, filename:String, fileSize: Long, destinationFile: File, finalDestinationFile: File, subfolder:String, serverPath:String, localFolder: LocalFolder, audioTrack: AudioTrack?, episode: PodcastEpisode?) :DownloadItemPart {
|
||||
val destinationUri = Uri.fromFile(destinationFile)
|
||||
val finalDestinationUri = Uri.fromFile(finalDestinationFile)
|
||||
|
||||
@@ -46,6 +48,7 @@ data class DownloadItemPart(
|
||||
id = DeviceManager.getBase64Id(finalDestinationFile.absolutePath),
|
||||
downloadItemId,
|
||||
filename = filename,
|
||||
fileSize = fileSize,
|
||||
finalDestinationPath = finalDestinationFile.absolutePath,
|
||||
serverPath = serverPath,
|
||||
localFolderName = localFolder.name,
|
||||
@@ -62,14 +65,12 @@ data class DownloadItemPart(
|
||||
finalDestinationUri = finalDestinationUri,
|
||||
finalDestinationSubfolder = subfolder,
|
||||
downloadId = null,
|
||||
progress = 0
|
||||
progress = 0,
|
||||
bytesDownloaded = 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@get:JsonIgnore
|
||||
val fileSize get() = audioTrack?.metadata?.size ?: 0
|
||||
|
||||
@JsonIgnore
|
||||
fun getDownloadRequest(): DownloadManager.Request {
|
||||
val dlRequest = DownloadManager.Request(uri)
|
||||
|
||||
@@ -145,6 +145,7 @@ class AbsDownloader : Plugin() {
|
||||
|
||||
// Create download item part for each audio track
|
||||
tracks.forEach { audioTrack ->
|
||||
val fileSize = audioTrack.metadata?.size ?: 0
|
||||
val serverPath = "/s/item/${libraryItem.id}/${cleanRelPath(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}")
|
||||
@@ -162,13 +163,16 @@ class AbsDownloader : Plugin() {
|
||||
finalDestinationFile.delete()
|
||||
}
|
||||
|
||||
val downloadItemPart = DownloadItemPart.make(downloadItem.id, destinationFilename,destinationFile,finalDestinationFile,itemSubfolder,serverPath,localFolder,audioTrack,null)
|
||||
val downloadItemPart = DownloadItemPart.make(downloadItem.id, destinationFilename, fileSize, destinationFile,finalDestinationFile,itemSubfolder,serverPath,localFolder,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 }
|
||||
val coverFileSize = coverLibraryFile?.metadata?.size ?: 0
|
||||
|
||||
val serverPath = "/api/items/${libraryItem.id}/cover"
|
||||
val destinationFilename = "cover-${libraryItem.id}.jpg"
|
||||
val destinationFile = File("$tempFolderPath/$destinationFilename")
|
||||
@@ -184,7 +188,7 @@ class AbsDownloader : Plugin() {
|
||||
finalDestinationFile.delete()
|
||||
}
|
||||
|
||||
val downloadItemPart = DownloadItemPart.make(downloadItem.id, destinationFilename,destinationFile,finalDestinationFile,itemSubfolder,serverPath,localFolder,null,null)
|
||||
val downloadItemPart = DownloadItemPart.make(downloadItem.id, destinationFilename, coverFileSize, destinationFile,finalDestinationFile,itemSubfolder,serverPath,localFolder,null,null)
|
||||
downloadItem.downloadItemParts.add(downloadItemPart)
|
||||
}
|
||||
|
||||
@@ -195,6 +199,8 @@ class AbsDownloader : Plugin() {
|
||||
val podcastTitle = cleanStringForFileSystem(libraryItem.media.metadata.title)
|
||||
|
||||
val audioTrack = episode?.audioTrack
|
||||
val fileSize = audioTrack?.metadata?.size ?: 0
|
||||
|
||||
Log.d(tag, "Starting podcast episode download")
|
||||
val itemFolderPath = localFolder.absolutePath + "/" + podcastTitle
|
||||
val downloadItemId = "${libraryItem.id}-${episode?.id}"
|
||||
@@ -211,10 +217,13 @@ class AbsDownloader : Plugin() {
|
||||
finalDestinationFile.delete()
|
||||
}
|
||||
|
||||
var downloadItemPart = DownloadItemPart.make(downloadItem.id, destinationFilename,destinationFile,finalDestinationFile,podcastTitle,serverPath,localFolder,audioTrack,episode)
|
||||
var downloadItemPart = DownloadItemPart.make(downloadItem.id, destinationFilename,fileSize, destinationFile,finalDestinationFile,podcastTitle,serverPath,localFolder,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 }
|
||||
val coverFileSize = coverLibraryFile?.metadata?.size ?: 0
|
||||
|
||||
serverPath = "/api/items/${libraryItem.id}/cover"
|
||||
destinationFilename = "cover.jpg"
|
||||
|
||||
@@ -224,7 +233,7 @@ class AbsDownloader : Plugin() {
|
||||
if (finalDestinationFile.exists()) {
|
||||
Log.d(tag, "Podcast cover already exists - not downloading cover again")
|
||||
} else {
|
||||
downloadItemPart = DownloadItemPart.make(downloadItem.id, destinationFilename,destinationFile,finalDestinationFile,podcastTitle,serverPath,localFolder,null,null)
|
||||
downloadItemPart = DownloadItemPart.make(downloadItem.id, destinationFilename,coverFileSize,destinationFile,finalDestinationFile,podcastTitle,serverPath,localFolder,null,null)
|
||||
downloadItem.downloadItemParts.add(downloadItemPart)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user