Resume HLS download with changed URL (AVAssetDownloadURLSession)

345 views Asked by At

Need some help here from strong AVKit'ers and AVFoundation'ers

I'm struggling to resume previously partly downloaded HLS stream.

URL, that I have, changes every 24H. And if user tries to resume download - it just starts again and ignores earlier download progress.

I use code from apple - Using AVFoundation to Play and Persist HTTP Live Streams

For now it works like that:

  1. Stream start download via assetDownloadURLSession.aggregateAssetDownloadTask
  2. User presses Pause. In this situation I save all progress to local file and use bookmarkData() to not lose that local URL:
let bookmark = try tempDownloadUrl?.bookmarkData()
userDefaults.set(bookmark, forKey: assetId)
  1. Link is dead, user taps Resume -> didFinishDownloadingTo from AVAssetDownloadDelegate immediately fires with 403 - Forbidden.
  2. I try to restart download task with creating new AVAggregateAssetDownloadTask using assetDownloadURLSession.aggregateAssetDownloadTask and inserting there local URL to file (.movpkg). If link doesn't change, everything works as expected.
  3. We are here

Some thoughts:

  • Probably we can somehow copy downloaded amount if m3u8 chunks to new download task
  • Probably we can change some kind of serverURL inside that previously downloaded .movpkg file.

Or something new. (I can share a link that dies after 2 minutes for testing purposes)

Thanks in advance.

1

There are 1 answers

0
Pete Streem On

Okay. I figured out how to deal with such a complex task.

Main thing is to update dead NetworkUrl inside our local .movpkg file to a new shiny NetworkURL.

Firstly we need to read contents of our local file .movpkg file.

 let subDirectories = try FileManager.default.contentsOfDirectory(
    at: ourLocalUrl, 
    includingPropertiesForKeys: nil, 
    options: .skipsSubdirectoryDescendants
 )

Nice, now we need to iterate through local directory and to find .xml and .frag files, that are holding our old url. This can be done in such way

[Sorry for some messy code, it is not production ready code, it's just points to a right direction of problem solving. Please refactor it on your own purposes]:

for url in subDirectories {
   var isDirectory: ObjCBool = false 
   if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDirectory) {
     if isDirectory.boolValue {
       debugPrint("[Searched FOLDER]\(url)")
       let newDirectories = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: .skipsSubdirectoryDescendants)
       var isNewDirectory: ObjCBool = false
       for newUrl in newDirectories {
         if FileManager.default.fileExists(atPath: newUrl.path, isDirectory: &isNewDirectory) {
            if isNewDirectory.boolValue {
               debugPrint("[Searched FOLDER DIRECTORY]\(newUrl)")
            } else {
               if var fileContent = try? String.init(contentsOf: URL.init(fileURLWithPath: newUrl.path)) {
                  fileContent = fileContent.replacingOccurrences(of: oldUrlString, with: newUrlString)
                  try? fileContent!.write(toFile: url.path, atomically: true, encoding: .utf8)
               }
            }
     } else {
       debugPrint("[Searched FILE]\(url)")
       var fileContent = try? String.init(contentsOf: URL.init(fileURLWithPath: url.path))
       fileContent! = fileContent!.replacingOccurrences(of: oldUrlString, with: newUrlString)
       try? fileContent!.write(toFile: url.path, atomically: true, encoding: .utf8)
     }
   }
}

So, in code above we search for our oldUrlString and replace it with newUrlString. You can obtain oldUrlString also from xml file or from CoreData (if you store oldUrl somewhere).

Make to remove last path component from oldUrlString and newUrlString before update. So link is similar to:

aaaa/xxxx123.smil

And not

xxxx123.smil/chunklist_b1728000.m3u8

It's because chunks are placed like that and replace will not work, if you will not remove chunklist_b1728000.m3u8 from path:

xxxx123.smil/chunk01.ts
xxxx123.smil/chunk02.ts
xxxx123.smil/chunk04.ts
...

That's it. After URL is replaced, resume downloading successfully achieved.