Fix failedList removal in deleteAsync and narrow task exception handler

- deleteAsync removed a failedList entry from the downloadQueue (a no-op:
  failed items were already polled out of the queue), leaving stale FAILED
  state for deleted tracks. Remove the entry from failedList instead.
- The DownloadTask CoroutineExceptionHandler deleted the complete and
  pinned files on any uncaught exception. Those files may already hold a
  finalized download (exceptions can occur in post-rename steps such as
  the catalog write), so this destroyed finished data. Only the partial
  file is deleted now: retries do not append to a possibly untrustworthy
  stream, while finalized downloads and cancelled (resumable) downloads
  are left untouched.
This commit is contained in:
birdbird committed 2026-10-02 11:21:28 +00:00
1 parent bac11ad2c4
commit 2c0ce5bb28
2 files changed
+9 -3

No files matched your search

@@ -423,7 +423,7 @@ class DownloadService :
private suspend fun deleteAsync(track: Track) {
withContext(Dispatchers.IO) {
downloadQueue.get(track.id)?.let { downloadQueue.remove(it) }
failedList[track.id]?.let { downloadQueue.remove(it) }
failedList.remove(track.id)
cancelDownload(track)
Storage.delete(track.getPartialFile())
@@ -206,8 +206,14 @@ class DownloadTask(
exception
->
Timber.w(exception, "Exception in DownloadTask ${downloadTrack.pinnedFile}")
Storage.delete(downloadTrack.completeFile)
Storage.delete(downloadTrack.pinnedFile)
// Only ever touch the partial file here: the complete/pinned files may hold a
// download that has already been finalized (the exception may have happened
// during a post-rename step such as the catalog write), so deleting them would
// destroy finished data. The partial file is removed so that a retry does not
// append to a possibly untrustworthy stream; pure cancellation keeps the partial
// file, because the exception handler is not invoked for CancellationException
// and cancelled downloads should remain resumable.
Storage.delete(downloadTrack.partialFile)
}
fun start() {