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 = request.videoTracks[0].renditions[0] {
        res = request.selectVideoRendition(videoRendition)
    }
    if let audioRendition = request.audioTracks[0].renditions[0] {
        res = request.selectAudioRendition(audioRendition)
    }

    let delegate = DownloadDelegate({})
    let error = downloader.startDownload(download, delegate: delegate)
}
```

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: MediaDownload) {
        if (download.state == .success) {
            complete()
        }
    }

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

## 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, it's possible to prefetch the license before starting the download. It's also possible to get the license after the download has completed (and without even starting the 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, it's not possible to know in advance the entire download size due to an API limitation. The download progress will restart from zero for each additional track. You can however differentiate between the main download (which includes the minimum set of tracks that can be played) and the additional tracks 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 measure download progress in duration of the stream that is cached.
The PRESTOplay SDK uses average bandwith provided in the main playlist to estimate the download size and progress.

Since `AVERAGE-BANDWIDTH` is optional part of HLS playlist, the SDK fallbacks to mandatory `BANDWIDTH` field.
`BANDWITH` is a peak segment bit rate, so the download sizes can be overestimated (please see HLS Streaming standard to details).

We recommend to use 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 an 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)
        }
    }
}
```
