diff --git a/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/AppComponent.kt b/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/AppComponent.kt index 15d37c4..d65d00e 100644 --- a/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/AppComponent.kt +++ b/desktop/app/src/main/kotlin/com/abdownloadmanager/desktop/AppComponent.kt @@ -34,6 +34,7 @@ import ir.amirab.downloader.utils.ExceptionUtils import ir.amirab.downloader.utils.OnDuplicateStrategy import com.abdownloadmanager.integration.Integration import com.abdownloadmanager.integration.IntegrationResult +import ir.amirab.downloader.exception.TooManyErrorException import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.serialization.Serializable @@ -274,43 +275,11 @@ class AppComponent( ) ) } - init { downloadSystem .downloadEvents .onEach { - if (it.context[ResumedBy]?.by !is User){ - //only notify events that is started by user - return@onEach - } -// or -// val qm = downloadSystem.queueManager -// val queueId = qm.findItemInQueue(it.downloadItem.id) -// if (queueId != null) { -// return@onEach -// // skip download events when download is triggered by queue -//// if (qm.getQueue(queue).isQueueActive){ -//// return@onEach -//// } -// } - if (it is DownloadManagerEvents.OnJobCanceled) { - if (!ExceptionUtils.isNormalCancellation(it.e)) { - sendNotification( - "downloadId=${it.downloadItem.id}", - title = it.downloadItem.name, - description = "Error" + it.e.localizedMessage?.let { " $it" }.orEmpty(), - type = NotificationType.Error, - ) - } - } - if (it is DownloadManagerEvents.OnJobCompleted) { - sendNotification( - tag = "downloadId=${it.downloadItem.id}", - title = it.downloadItem.name, - description = "Finished", - type = NotificationType.Success, - ) - } + onNewDownloadEvent(it) } .launchIn(scope) // IntegrationPortBroadcaster.cleanOnClose() @@ -337,6 +306,57 @@ class AppComponent( }.launchIn(scope) } + private fun onNewDownloadEvent(it: DownloadManagerEvents) { + if (it.context[ResumedBy]?.by !is User){ + //only notify events that is started by user + return + } +// or +// val qm = downloadSystem.queueManager +// val queueId = qm.findItemInQueue(it.downloadItem.id) +// if (queueId != null) { +// return@onEach +// // skip download events when download is triggered by queue +//// if (qm.getQueue(queue).isQueueActive){ +//// return@onEach +//// } +// } + if (it is DownloadManagerEvents.OnJobCanceled) { + val exception = it.e + if (ExceptionUtils.isNormalCancellation(exception)) { + return + } + var isMaxTryReachedError = false + val actualCause = if (exception is TooManyErrorException){ + isMaxTryReachedError=true + exception.findActualDownloadErrorCause() + }else exception + if (ExceptionUtils.isNormalCancellation(actualCause)) { + return + } + val prefix = if (isMaxTryReachedError) { + "Too Many Error: " + }else{ + "Error: " + } + val reason = actualCause.message?:"Unknown" + sendNotification( + "downloadId=${it.downloadItem.id}", + title = it.downloadItem.name, + description = prefix + reason, + type = NotificationType.Error, + ) + } + if (it is DownloadManagerEvents.OnJobCompleted) { + sendNotification( + tag = "downloadId=${it.downloadItem.id}", + title = it.downloadItem.name, + description = "Finished", + type = NotificationType.Success, + ) + } + } + override fun openAddDownloadDialog( links: List ) { diff --git a/downloader/core/src/main/kotlin/ir/amirab/downloader/exception/PartTooManyErrorException.kt b/downloader/core/src/main/kotlin/ir/amirab/downloader/exception/PartTooManyErrorException.kt index 1ed88bf..4b5800b 100644 --- a/downloader/core/src/main/kotlin/ir/amirab/downloader/exception/PartTooManyErrorException.kt +++ b/downloader/core/src/main/kotlin/ir/amirab/downloader/exception/PartTooManyErrorException.kt @@ -3,5 +3,9 @@ package ir.amirab.downloader.exception import ir.amirab.downloader.part.Part class PartTooManyErrorException( - part: Part -) : Exception("this part $part have too many exception") + part: Part, + override val cause: Throwable +) : Exception( + "this part $part have too many errors", + cause, +) diff --git a/downloader/core/src/main/kotlin/ir/amirab/downloader/exception/TooManyErrorException.kt b/downloader/core/src/main/kotlin/ir/amirab/downloader/exception/TooManyErrorException.kt index 1ea45cb..6c0e908 100644 --- a/downloader/core/src/main/kotlin/ir/amirab/downloader/exception/TooManyErrorException.kt +++ b/downloader/core/src/main/kotlin/ir/amirab/downloader/exception/TooManyErrorException.kt @@ -1,8 +1,14 @@ package ir.amirab.downloader.exception class TooManyErrorException( - lastException: Throwable, + override val cause: Throwable ) : Exception( "Download is stopped because all parts exceeds max retries", - lastException, -) +) { + fun findActualDownloadErrorCause(): Throwable { + return when (cause) { + is PartTooManyErrorException -> cause.cause + else -> cause + } + } +} diff --git a/downloader/core/src/main/kotlin/ir/amirab/downloader/part/PartDownloader.kt b/downloader/core/src/main/kotlin/ir/amirab/downloader/part/PartDownloader.kt index 4fd113d..86202cb 100644 --- a/downloader/core/src/main/kotlin/ir/amirab/downloader/part/PartDownloader.kt +++ b/downloader/core/src/main/kotlin/ir/amirab/downloader/part/PartDownloader.kt @@ -83,6 +83,11 @@ class PartDownloader( @Volatile internal var tries = 0 + // make sure to not lake resource in this exception + @Volatile + private var lastCriticalException: Throwable? = null + + // make sure to not lake resource in this exception @Volatile private var lastException: Throwable? = null @@ -100,6 +105,7 @@ class PartDownloader( } scope.launch { tries = 0 + lastCriticalException = null lastException = null val result = kotlin.runCatching { while (coroutineContext.isActive || !stop) { @@ -110,7 +116,10 @@ class PartDownloader( if (haveToManyErrors()) { // println("tell them we have error!") iCantRetryAnymore( - PartTooManyErrorException(part) + PartTooManyErrorException( + part, + lastException?:Exception("BUG : if you see me please report it to the developer! when we encounter error so it have to be a least one last exception"), + ) ) } if (part.isCompleted) { @@ -190,7 +199,7 @@ class PartDownloader( lateinit var onTooManyErrors: ((Throwable) -> Unit) private fun iCantRetryAnymore(throwable: Throwable) { - lastException = throwable + lastCriticalException = throwable GlobalScope.launch { onTooManyErrors(throwable) } @@ -201,7 +210,7 @@ class PartDownloader( } private fun haveCriticalError(): Boolean { - return lastException != null + return lastCriticalException != null } internal fun injured(): Boolean { @@ -333,6 +342,7 @@ class PartDownloader( } private fun onCanceled(e: Throwable) { + lastException = e val canceled = PartDownloadStatus.Canceled(e) onNewStatus(canceled) e.printStackIfNOtUsual()