mirror of
https://github.com/amir1376/ab-download-manager.git
synced 2025-02-20 11:43:24 +08:00
add use server last-modified time
This commit is contained in:
parent
2c19492d61
commit
1395316a92
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -48,6 +48,7 @@ class SimpleDownloadDestination(
|
||||
}
|
||||
|
||||
override fun onAllFilePartsRemoved() {
|
||||
super.onAllFilePartsRemoved()
|
||||
// println("release handle")
|
||||
removeFileHandle()
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user