Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Resuming HLS download and deleting partially downloaded file

I am implementing HLS streaming as per Apple Docs

But the problem that I am facing is for resuming the download when the user kills the app. If a download is in progress and say its 50% done and the user kills the app or app is killed by the system due to any reason and when the app is alive again then the URL session delegate of didCompleteWithError is called

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
}

and here I dont have the partially downloaded file path or ability to resume the task.

The only location for the downloaded file is called when the download is complete via the following delegate call

func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didFinishDownloadingTo location: URL) {
}

Now doc says to use

downloadSession.getAllTasks { tasksArray in }

but unfortunately, it does not resume the download

So my problem is

  1. How to resume the task from that downloaded state so that the entire download doesn't start all over again from 0% ?
  2. For the task that is not resumable or for a particular scenario where I don't want to resume it how can I delete the partially downloaded file ? How will I get the downloaded path (I don't want to search the entire documents directory)
like image 447
Girish Nair Avatar asked Apr 18 '19 14:04

Girish Nair


1 Answers

Actually, you can use getAllTasks(completionHandler:)] to get pending tasks which are not completed at the previous launch but somehow these tasks will be cancelled immediately after download session is created which leads to urlSession(_:task:didCompleteWithError:) is called as you see.

Luckily, I found another way to resume AVAssetDownloadTask

AVAssetDownloadTask provides the ability to resume previously stopped downloads under certain circumstances. To do so, simply instantiate a new AVAssetDownloadTask with an AVURLAsset instantiated with a file NSURL pointing to the partially downloaded bundle with the desired download options, and the download will continue restoring any previously downloaded data.

It means that if you want to resume a pending AVAssetDownloadTask, you have to save location from urlSession(_:assetDownloadTask:didFinishDownloadingTo:) when download task is stopped. After that, create another download task base on the partially downloaded file.

func urlSession(_ session: URLSession, assetDownloadTask: AVAssetDownloadTask, didFinishDownloadingTo location: URL) {
    destinationURL = location
}

func resumeDownloadTask() {
    let urlAsset = AVURLAsset(url: destinationURL)
    downloadTask = assetDownloadURLSession.makeAssetDownloadTask(asset: urlAsset, assetTitle: "title", assetArtworkData: nil, options: nil)
    downloadTask.resume()
}

urlSession(_:assetDownloadTask:didFinishDownloadingTo:) will always be called before urlSession(_:task:didCompleteWithError:) so you can get destinationURL in both case when app is terminated and relaunch or download task is cancelled.

Note that you shouldn't create new download task inside urlSession(_:task:didCompleteWithError:), somehow it will lead to an infinity loop.

With your second question, simply delete the file by using destinationURL.

For more detail, I created a sample repo at the below link. There is still some bug but it can run in normal case. Try to start download task, let it runs for a while and terminated the app. Relaunch and resume the task, you will see the result.

https://github.com/trungducc/stackoverflow/tree/hls-download-resuming

like image 171
trungduc Avatar answered Nov 13 '22 02:11

trungduc