diff --git a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt index 3a8498f5..134a30c1 100644 --- a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt +++ b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt @@ -156,6 +156,8 @@ class DepotDownloader @JvmOverloads constructor( private val progressUpdateInterval = 500L // ms + private var lastProgressUpdate = 0L + private val scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob(parentJob)) private var cdnClientPool: CDNClientPool? = null @@ -211,6 +213,7 @@ class DepotDownloader @JvmOverloads constructor( val androidEmulation: Boolean = false, val downloadManifestOnly: Boolean = false, val installToGameNameDirectory: Boolean = false, + val downloadPath: Path? = null, // Not used yet in code val usingFileList: Boolean = false, @@ -1571,7 +1574,8 @@ class DepotDownloader @JvmOverloads constructor( ensureActive() // Create temporary file path for this chunk - val chunkTempDir = depot.installDir / STAGING_DIR / "chunks" / fileId + // Try to set it to the Download path, use installdir as fallback + val chunkTempDir = (config.downloadPath ?: depot.installDir) / STAGING_DIR / "chunks" / fileId filesystem.createDirectories(chunkTempDir) val chunkTempPath = chunkTempDir / "${chunk.offset}_$chunkID.chunk" @@ -1745,6 +1749,8 @@ class DepotDownloader @JvmOverloads constructor( } is AppItem -> { + config = config.copy(downloadPath = item.downloadDirectory?.toPath()) + val branch = item.branch ?: DEFAULT_BRANCH config = config.copy(betaPassword = item.branchPassword) @@ -1835,7 +1841,7 @@ class DepotDownloader @JvmOverloads constructor( val file = item.file val fileFinalPath = depot.installDir / file.fileName val chunk = item.chunk - val chunkTempDir = depot.installDir / STAGING_DIR / "chunks" / item.fileId + val chunkTempDir = (config.downloadPath ?: depot.installDir) / STAGING_DIR / "chunks" / item.fileId val writeOffset = file.chunks.filter { it.offset < chunk.offset }.sumOf { it.uncompressedLength.toLong() } @@ -1888,14 +1894,19 @@ class DepotDownloader @JvmOverloads constructor( } val depotPercentage = (sizeDownloaded.toFloat() / depotDownloadCounter.completeDownloadSize) - - notifyListeners { listener -> - listener.onChunkCompleted( - depotId = depot.depotId, - depotPercentComplete = depotPercentage, - compressedBytes = downloadCounter.totalBytesCompressed, - uncompressedBytes = downloadCounter.totalBytesUncompressed - ) + // Limiting the listener calls to once every 0.5s significantly improves performance on SD cards + val now = System.currentTimeMillis() + if (now - lastProgressUpdate >= progressUpdateInterval) { + lastProgressUpdate = now + logger?.debug("Chunk progress update") + notifyListeners { listener -> + listener.onChunkCompleted( + depotId = depot.depotId, + depotPercentComplete = depotPercentage, + compressedBytes = downloadCounter.totalBytesCompressed, + uncompressedBytes = downloadCounter.totalBytesUncompressed + ) + } } val remainingDownloads = item.fileStreamData.chunksDownloaded.decrementAndGet() diff --git a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/data/DownloadItems.kt b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/data/DownloadItems.kt index 813e0b52..d3cceda7 100644 --- a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/data/DownloadItems.kt +++ b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/data/DownloadItems.kt @@ -70,6 +70,7 @@ class PubFileItem @JvmOverloads constructor( * @property lowViolence If true, downloads low-violence versions where available * @property depot List of specific depot IDs to download * @property manifest List of specific manifest IDs corresponding to depot IDs + * @property downloadDirectory Separate directory for downloading and staging an app/game * * @author Lossy * @since Oct 1, 2025 @@ -89,6 +90,7 @@ class AppItem @JvmOverloads constructor( val lowViolence: Boolean = false, val depot: List = emptyList(), val manifest: List = emptyList(), + val downloadDirectory: String? = null, verify: Boolean = false, downloadManifestOnly: Boolean = false, ) : DownloadItem(appId, installDirectory, installToGameNameDirectory, verify, downloadManifestOnly) diff --git a/javasteam-samples/src/main/java/in/dragonbra/javasteamsamples/_023_downloadapp/SampleDownloadApp.java b/javasteam-samples/src/main/java/in/dragonbra/javasteamsamples/_023_downloadapp/SampleDownloadApp.java index 1305d187..0b62b5fb 100644 --- a/javasteam-samples/src/main/java/in/dragonbra/javasteamsamples/_023_downloadapp/SampleDownloadApp.java +++ b/javasteam-samples/src/main/java/in/dragonbra/javasteamsamples/_023_downloadapp/SampleDownloadApp.java @@ -290,6 +290,7 @@ private void downloadApp() { /* (Optional) lowViolence */ false, /* (Optional) depot */ List.of(), /* (Optional) manifest */ List.of(), + /* (Optional) downloadDirectory */ null, /* (Optional) verify */ false, /* (Optional) downloadManifestOnly */ false );