We developed different downloaders for handling HLS/FPS and MPEG-DASH content. They can all be used behind the common interface `DownloaderAPI`

## DownloaderAPI

The main data type in `DownloaderAPI` is a `Download`.

```swift
let url = URL(string: "..")!
let config = PlayerConfiguration(with: url, contentType: "...")

let downloader = PRESTOplaySDK.shared.downloader()
let download = downloader.createDownload(config, headers: [:])
downloader.prepareDownload(download.uuid) { download, error in
    guard let download else { return }
    var res = false

    if let videoRendition = download.videoTracks.first?.renditions.first {
        res = download.selectVideoRendition(videoRendition)
    }
    if let audioRendition = download.audioTracks.first?.renditions.first {
        res = download.selectAudioRendition(audioRendition)
    }

    if let error = downloader.startDownload(download.uuid, delegate: nil) {
        print("Start error: \(error)")
    }
}
```

To receive events related to a `Download` we use a delegate:

```swift
class MyDownloadDelegate: DownloadDelegate {
    public let complete: () -> Void

    public init (_ complete: @escaping () -> Void) {
        self.complete = complete
    }

    func didStateChange(_ download: Download) {
        if download.state == .success {
            complete()
        }
    }

    func didProgressChange(_ download: Download) {
    }
}
```

## License

If you want to prefetch the license without forcing the user to be online during the first playback, use the method _PRESTOplaySDK.shared.prefetcher(for:)_

```swift
let prefetcher = PRESTOplaySDK.shared.prefetcher(for: config)
prefetcher?.prefetchKeys() { error in
    if error != nil {
        // Handle error
    }
}
```

Firstly, the prefetcher searches for keys in `Multivariant Playlist` in `EXT-X-SESSION-KEY`.
If keys are found the prefetcher downloads the listed keys.
If keys are not found the prefetcher search for `EXT-X-KEY` tags in:

- variant playlists (`EXT-X-STREAM-INF`)
- rendition tracks (`EXT-X-MEDIA`)
- iframe tracks (`EXT-X-I-FRAME-STREAM-INF`)

## HLS streams

For downloading HLS and FPS we created a wrapper around the native HLS downloader `AVAssetDownloadTask`

- enables querying and selection of stream bitrate
- enables querying of audio media streams and subtitle media tracks
- adds support for pre-selecting audio and subtitle tracks before download begins
- greatly simplifies API usage and the monitoring of downloading progress.
- supports preselection of the preferred media bitrate. If no suitable media bitrate is found, the highest bitrate will be selected. Only one media bitrate can be selected, meaning that only one video rendition will be downloaded
- background downloads are fully supported
- If the content is encrypted, you can prefetch the license before starting the download. You can also get the license after the download has completed (and without starting playback).

Background download tasks are handled by `NSURLSession` underneath and there are differences between the test environment and the production environment.

For background downloads an instance of HLSDownloader should be kept in AppDelegate. This is the only supported way from Apple to preserve _completionHandlers_ of background sessions in-between Application Lifecycle changes (in our example we use static class field, but it is also possible to use object variables in AppDelegate).

When additional tracks are selected, the complete download size is not known in advance due to an API limitation. The download progress restarts from zero for each additional track. You can still differentiate between the main download (which includes the minimum set of tracks that can be played) and additional tracks by using the flag.

When using a master playlist, the CODECS attribute of the EXT-X-STREAM-INF tag may signal CODECs not supported for playback on the downloading device, but download of such streams will be possible. Check [Apple FAQ](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StreamingMediaGuide/FrequentlyAskedQuestions/FrequentlyAskedQuestions.html) for an updated list of recommended codecs.

### HLS download progress

Natively, Apple's API measures download progress by stream duration cached.
The PRESTOplay SDK uses average bandwidth provided in the main playlist to estimate download size and progress.

Since `AVERAGE-BANDWIDTH` is an optional part of the HLS playlist, the SDK falls back to the mandatory `BANDWIDTH` field.
`BANDWIDTH` is a peak segment bit rate, so download sizes can be overestimated (see the HLS streaming standard for details).

We recommend using native time-based download progress measures for HLS content.

## MPEG-DASH streams

DASH streams are downloaded using `NSURLSession` with custom manifest parser library.

## Additional track selection

Additional track selection can be done on `Download` class instance.
We encourage to store all possible selection in your `ViewController` class.

```swift
var audioTracks = [AudioTrack]()
var audioRenditions = [AudioRendition]()
var textTracks = [TextTrack]()
var videoRenditions = [VideoRendition]()
```

First you need to create the download.

```swift
let download = downloader.createDownload(persistableConfiguration, headers: [:])
```

When `prepareDownload` step is finished you can get the list of all available to download tracks.

```swift
downloader.prepareDownload(download.uuid) { download, error in

    ...

    download.audioTracks.forEach { track in
        track.renditions.forEach { rendition in
            self.audioRenditions.append(rendition)
        }
    }
    self.audioTracks = download.audioTracks
    self.textTracks = download.textTracks

    download.videoTracks.forEach { track in
        track.renditions.forEach { rendition in
            self.videoRenditions.append(rendition)
        }
    }

    ...

}
```

Below you can find a universal method to select and unselect tracks for the download (for native HLS, only the default video track is selected).

```swift
func selectTrack(_ indexPath: IndexPath , isSelected: Bool) {
    if indexPath.section == audioSectionIndex {
        let rendition = audioRenditions[indexPath.row]
        if isSelected {
            _ = download?.selectAudioRendition(rendition)
        } else {
            _ = download?.unselectAudioRendition(rendition)
        }
    } else if indexPath.section == textSectionIndex {
        let textTrack = textTracks[indexPath.row]
        if isSelected {
            _ = download?.selectTextTrack(textTrack)
        } else {
            _ = download?.unselectTextTrack(textTrack)
        }
    } else if indexPath.section == renditionSectionIndex {
        let rendition = videoRenditions[indexPath.row]
        if isSelected {
            _ = download?.selectVideoRendition(rendition)
        } else {
            _ = download?.unselectVideoRendition(rendition)
        }
    }
}
```
