add use server last-modified time

This commit is contained in:
AmirHossein Abdolmotallebi 2024-08-29 12:32:38 +03:30
parent 2c19492d61
commit 1395316a92
8 changed files with 81 additions and 5 deletions

View File

@ -62,6 +62,21 @@ fun dynamicPartDownloadConfig(appRepository: AppRepository): BooleanConfigurable
)
}
fun useServerLastModified(appRepository: AppRepository): BooleanConfigurable {
return BooleanConfigurable(
title = "Server's Last-Modified Time",
description = "When downloading a file, use server's last modified time for the local file",
backedBy = appRepository.useServerLastModifiedTime,
describe = {
if (it) {
"Enabled"
} else {
"Disabled"
}
},
)
}
fun speedLimitConfig(appRepository: AppRepository): SpeedLimitConfigurable {
return SpeedLimitConfigurable(
title = "Global Speed Limiter",
@ -247,7 +262,8 @@ class SettingsComponent(
useAverageSpeedConfig(appRepository),
speedLimitConfig(appRepository),
threadCountConfig(appRepository),
dynamicPartDownloadConfig(appRepository)
dynamicPartDownloadConfig(appRepository),
useServerLastModified(appRepository)
)
}
}

View File

@ -31,6 +31,7 @@ class AppRepository : KoinComponent {
val speedLimiter = appSettings.speedLimit
val threadCount = appSettings.threadCount
val dynamicPartCreation = appSettings.dynamicPartCreation
val useServerLastModifiedTime = appSettings.useServerLastModifiedTime
val useAverageSpeed = appSettings.useAverageSpeed
val saveLocation = appSettings.defaultDownloadFolder
val integrationEnabled = appSettings.browserIntegrationEnabled
@ -66,6 +67,12 @@ class AppRepository : KoinComponent {
downloadSettings.dynamicPartCreationMode = it
downloadManager.reloadSetting()
}.launchIn(scope)
useServerLastModifiedTime
.debounce(500)
.onEach {
downloadSettings.useServerLastModifiedTime = it
downloadManager.reloadSetting()
}.launchIn(scope)
integrationPort
.debounce(500)
.onEach {

View File

@ -18,6 +18,7 @@ data class AppSettingsModel(
val theme: String = "dark",
val threadCount: Int = 5,
val dynamicPartCreation: Boolean = true,
val useServerLastModifiedTime: Boolean = false,
val useAverageSpeed: Boolean = true,
val speedLimit: Long = 0,
val autoStartOnBoot: Boolean = true,
@ -37,6 +38,7 @@ data class AppSettingsModel(
val theme = stringKeyOf("theme")
val threadCount = intKeyOf("threadCount")
val dynamicPartCreation = booleanKeyOf("dynamicPartCreation")
val useServerLastModifiedTime = booleanKeyOf("useServerLastModifiedTime")
val useAverageSpeed = booleanKeyOf("useAverageSpeed")
val speedLimit = longKeyOf("speedLimit")
val autoStartOnBoot = booleanKeyOf("autoStartOnBoot")
@ -54,6 +56,7 @@ data class AppSettingsModel(
theme = source.get(Keys.theme) ?: default.theme,
threadCount = source.get(Keys.threadCount) ?: default.threadCount,
dynamicPartCreation = source.get(Keys.dynamicPartCreation) ?: default.dynamicPartCreation,
useServerLastModifiedTime = source.get(Keys.useServerLastModifiedTime) ?: default.useServerLastModifiedTime,
useAverageSpeed = source.get(Keys.useAverageSpeed) ?: default.useAverageSpeed,
speedLimit = source.get(Keys.speedLimit) ?: default.speedLimit,
autoStartOnBoot = source.get(Keys.autoStartOnBoot) ?: default.autoStartOnBoot,
@ -70,6 +73,7 @@ data class AppSettingsModel(
put(Keys.theme, focus.theme)
put(Keys.threadCount, focus.threadCount)
put(Keys.dynamicPartCreation, focus.dynamicPartCreation)
put(Keys.useServerLastModifiedTime, focus.useServerLastModifiedTime)
put(Keys.useAverageSpeed, focus.useAverageSpeed)
put(Keys.speedLimit, focus.speedLimit)
put(Keys.autoStartOnBoot, focus.autoStartOnBoot)
@ -88,6 +92,7 @@ class AppSettingsStorage(
var theme = from(AppSettingsModel.theme)
val threadCount = from(AppSettingsModel.threadCount)
val dynamicPartCreation = from(AppSettingsModel.dynamicPartCreation)
val useServerLastModifiedTime = from(AppSettingsModel.useServerLastModifiedTime)
val useAverageSpeed = from(AppSettingsModel.useAverageSpeed)
val speedLimit = from(AppSettingsModel.speedLimit)
val autoStartOnBoot = from(AppSettingsModel.autoStartOnBoot)

View File

@ -3,7 +3,8 @@ package ir.amirab.downloader
data class DownloadSettings(
//can be changed after boot!
var defaultThreadCount: Int = 5,
var dynamicPartCreationMode:Boolean=true,
var globalSpeedLimit:Long=0,//unlimited
var dynamicPartCreationMode: Boolean = true,
var useServerLastModifiedTime: Boolean = false,
var globalSpeedLimit: Long = 0,//unlimited
val minPartSize: Long = 2048,//2kB
)

View File

@ -11,7 +11,12 @@ abstract class DownloadDestination(
protected val fileParts = mutableListOf<DestWriter>()
protected var allPartsDownloaded = false
protected open fun onAllFilePartsRemoved(){}
protected var requestedToChangeLastModified:Long?=null
protected open fun onAllFilePartsRemoved(){
updateLastModified()
}
open fun onAllPartsCompleted() {
allPartsDownloaded = true
cleanUpJunkFiles()
@ -67,4 +72,20 @@ abstract class DownloadDestination(
}
}
}
/**
* specify last modified time to be used when this destination finish its work
* for example file paused / finished
*/
fun setLastModified(timestamp: Long?) {
requestedToChangeLastModified=timestamp
}
protected fun updateLastModified(){
kotlin.runCatching {
requestedToChangeLastModified?.let {
outputFile.setLastModified(it)
}
}
}
}

View File

@ -48,6 +48,7 @@ class SimpleDownloadDestination(
}
override fun onAllFilePartsRemoved() {
super.onAllFilePartsRemoved()
// println("release handle")
removeFileHandle()
}

View File

@ -11,6 +11,7 @@ import ir.amirab.downloader.exception.FileChangedException
import ir.amirab.downloader.exception.TooManyErrorException
import ir.amirab.downloader.part.*
import ir.amirab.downloader.utils.ExceptionUtils
import ir.amirab.downloader.utils.TimeUtils
import ir.amirab.downloader.utils.printStackIfNOtUsual
import ir.amirab.downloader.utils.splitToRange
import kotlinx.coroutines.*
@ -49,6 +50,9 @@ class DownloadJob(
var supportsConcurrent: Boolean? = null
private set
var serverLastModified: Long? = null
private set
private val _isDownloadActive = MutableStateFlow(false)
val isDownloadActive = _isDownloadActive.asStateFlow()
@ -188,7 +192,8 @@ class DownloadJob(
}
// thisLogger().info("preparing file")
destination.prepareFile(onProgressUpdate)
val lastModified = serverLastModified.takeIf { downloadManager.settings.useServerLastModifiedTime }
destination.setLastModified(lastModified)
// thisLogger().info("file prepared")
}
}
@ -538,6 +543,9 @@ class DownloadJob(
// thisLogger().info("fetchDownloadInfoAndValidate")
val response = client.head(downloadItem).expectSuccess()
supportsConcurrent = response.resumeSupport
serverLastModified = kotlin.runCatching {
response.lastModified?.let(TimeUtils::convertLastModifiedHeaderToTimestamp)
}.getOrNull()
if (response.isWebPage()) {
if (isDownloadItemIsAWebpage()) {
// don't strict if it's a webpage let it download without checks

View File

@ -0,0 +1,17 @@
package ir.amirab.downloader.utils
import java.text.SimpleDateFormat
import java.util.*
object TimeUtils {
fun convertLastModifiedHeaderToTimestamp(lastModified: String): Long {
// Define the format of the Last-Modified header
val dateFormat = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US)
// Parse the date string into a Date object
val date = dateFormat.parse(lastModified)
// Convert the Date object to a timestamp (milliseconds since epoch)
return date?.time ?: throw IllegalArgumentException("Invalid Last-Modified header")
}
}