Skip to main content

Realization of photo album function

1. Overview

In this tutorial, you will learn how to use AUTEL Mobile SDK to access media resources in the SD card of the aircraft's camera or in the flash memory of the aircraft. By the end of this tutorial you will have an application that you can use to preview photos, play videos, download or delete files.

The following uses Autel_UX_SDK demo as an example to explain how to access the album, download, delete, or play a resource file in the album.

2. Call Flow Diagram

The following call flow diagram shows the interactions between the album UI layer, local album business manager class, and Mobile SDK.

Call Flow Diagram-EN

The following summarizes all APIs provided by Mobile SDK:

Album Bussiness

3. Album APIs

The album implementation details are encapsulated inside the Mobile SDK. To obtain the album manager class of the SD card, call DroneConnection.shared.drone?.camera.playbackManager(). To obtain the album manager class of the flash memory, call DroneConnection.shared.drone?.camera.mmcPlaybackManager() **. The DroneConnection class is a single instance class for aircraft object management, which exists in the AutelUXFramework** dynamic library.

If you do not want to use the DroneConnection.shared.drone method, you can use the ATConsoleViewController or ATConsoleViewModel class. They conform to CameraProtocol and DroneProtocol and can obtain the camera or aircraft status in real time or report parameters.

Note: If ATConsoleViewController or ATConsoleViewModel is not inherited from BaseViewController but from NSObject, you must call the following code to register the protocols. Otherwise, the protocols do not take effect. BaseViewController has already automatically been registered with the protocols. Therefore, no protocol needs to be registered again for classes inherited from it.

  func registerProtocol() {
registerAutelProtocolsIfCan()
}

You can deregister the protocols using the following code when the class is destroyed:

  func  removeProtocol() {
removeAutelProtocolsIfCan()
}

CameraProtocol:

public protocol CameraProtocol : AutelUXFramework.CheckProtocol {

func camera(_ camera: AUTELBaseCamera!, didUpdateConnectState connectState: AUTELCameraConnectState)

func camera(_ camera: AUTELBaseCamera!, didUpdateRecordRecoverState recoverState: AUTELCameraRecordRecoverState)

func camera(_ camera: AUTELBaseCamera!, didGenerateNewMediaFile newMedia: AUTELMedia!)

func camera(_ camera: AUTELBaseCamera!, didUpdateSDCardState sdCardState: AUTELCameraSDCardState!)

func camera(_ camera: AUTELBaseCamera!, didUpdateMMCState mmcState: AUTELCameraSDCardState!)

func camera(_ camera: AUTELBaseCamera!, didUpdateSystemState systemState: AUTELCameraSystemBaseState!)

func camera(_ camera: AUTELBaseCamera!, didUpdateParameters parameters: AUTELCameraParameters!, change: [AnyHashable : [Any]]!)

func camera(_ camera: AUTELBaseCamera!, didUpdateCurrentExposureValues exposureParameters: AUTELCameraExposureParameters!)

func camera(_ camera: AUTELBaseCamera!, didUpdateHistogramTotalPixels totalPixels: Int, andPixelsPerLevel pixelsArray: [Any]!)

func camera(_ camera: AUTELBaseCamera!, didUpdateTimeLapseCaptured captureCount: Int, andCountDown timeInterval: TimeInterval)

func camera(_ camera: AUTELBaseCamera!, didUpdateAutoFocusState focusState: AUTELCameraAFLensFocusState, andLensFocusAreaRowIndex rowIndex: Int, andColIndex colIndex: Int)

func camera(_ camera: AUTELBaseCamera!, didUpdateAeAfState state: AUTELCameraAFWorkStatus)
}

As long as you can get the instance object of AUTELBaseCamera, you can obtain the album manager class playbackManager or mmcPlaybackManager.

3.1 Obtain the Album Files

// Obtains the album files page by page (by default all types of resource files will be retrieved)
- (void)fetchMediaFileListFromOffset:(NSUInteger)offset
fileCount:(NSUInteger)count
withCompletion:(void (^_Nonnull)(NSArray<AUTELMedia *> *_Nullable mediaList, NSError *_Nullable error))block;
// Obtains the album files page by page (the caller transfers the type of the resource files to be retrieved)
- (void)fetchMediaFileListFromOffset:(NSUInteger)offset
fileCount:(NSUInteger)count
fileType:(AUTELGetFileListType)fileType
withCompletion:(void (^_Nonnull)(NSArray<AUTELMedia *> *_Nullable mediaList, NSError *_Nullable error))block;

After you obtain the album files, you can get an array of lists, which is [AUTELMedia]. AUTELMedia is the basic property of media files encapsulated by the Mobile SDK.

/**
* This class describes basic properties of media files stored in the SD card.
*
* This class contains information about a multi-media file on the SD card.
*/
@interface AUTELMedia : NSObject<NSCopying, NSCoding>

/**
* @brief Name of the media file.
*
* Returns the name of the media file.
*/
@property (nonatomic, readonly) NSString *fileName;

/**
* @brief Path to the media file in the SD card.
*
* Returns the file path of the media file in SD card.
*/
@property (nonatomic, readonly) NSString *filePath;

/**
* @brief Media file creation time, in the following format: yyyy-MM-dd HH:mm:ss.
*
* Returns the time when the media file was created as a string in
* the format "yyyy-MM-dd HH:mm:ss".
*/
@property (nonatomic, readonly) NSString *timeCreated;

/**
* @brief Index of the media file. Photos and videos that are newly taken are placed first in the album.
*
* Returns the index of the media file. The latest taken file will be lined in the front.
*/
@property (nonatomic, readonly) NSUInteger mediaIndex;

/**
* @brief Size of the media file, in bytes.
*
* Returns the size, in bytes, of the media file.
*/
@property (nonatomic, readonly) long long fileSizeInBytes;

/**
* @brief Type of the media file.
*
* Returns the type of media file.
*/
@property (nonatomic, readonly) AUTELMediaType mediaType;

/**
* @brief Unique identifier of the media file.
*
* Returns the unique identifier of media file.
*/
@property (nonatomic, readonly) NSString *uniqueID;

/**
Video duration
*/
@property (nonatomic, assign) NSUInteger videoDuration;

/**
@brief Resolution of the video file. For a photo file, the value of the videoResolution property is `AUTELCameraVideoResolutionUnknown`. For a video file, the initial value of the videoResolution property is also `AUTELCameraVideoResolutionUnknown`. However, the property will be set to the correct resolution if the thumbnail picture of the video file is returned.

Returns the resolution of video file.
*/
@property (nonatomic, readonly) AUTELCameraVideoResolution videoResolution;

/**
Returns the video framerate of video file.
*/
@property (nonatomic, readonly) AUTELCameraVideoFrameRate videoFrameRate;

/**
@brief Compression standard of the video file.

Returns the compreassion standar of video file.
*/
@property (nonatomic, readonly) AUTELCameraVideoFileCompressionStandard videoCompressionStandard;

/**
@brief Type of the panoramic photo.

Returns the Types of panoramic photos.
*/
@property (nonatomic, assign) AUTELCameraPanoramaType panoramaType;

/**
@brief Type of the shooting mission.

Returns the Types of mission photos.
*/
@property (nonatomic, assign) AUTELCameraMediaFileType fileType;

Enumeration parameters of AUTELGetFileListType

typedef NS_ENUM(NSInteger, AUTELGetFileListType) {
/**
* @brief Image file
*
* List of all files
*/
AUTELGetFileListTypeAll = 0,

/**
* @brief Video file list
*
* Video file list
*/
AUTELGetFileListTypeVideo = 1,

/**
* @brief Picture file list
*
* Photo file list
*/
AUTELGetFileListTypePhoto = 2,
};

3.2 Obtain the Thumbnail Picture of a File

- (void)fetchThumbnailForMedia:(AUTELMedia *_Nonnull)media
withCompletion:(AUTELLowerResolutionImageCompletion)block; // Obtains the thumbnail picture of a file.

3.3 Obtain the File Preview

- (void)fetchPreviewImageForMedia:(AUTELMedia *_Nonnull)media
withCompletion:(AUTELLowerResolutionImageCompletion)block;// Obtains the file preview.

3.4 Obtain Video Cache Data or Download a Video

- (void)playerItemForVideo:(AUTELMedia *_Nonnull)media
withCompletion:(void (^_Nullable)(AVPlayerItem *_Nullable playerItem, BOOL isOriginalData, NSError * _Nullable error))block;// Checks whether video cache data exists. If not, checks whether the original video file exists. If the original video file also does not exist, initiates the request for downloading the cache data.

Call example

func fetchPlayerItem(media: ATMediaItem, completion: @escaping (Result<AVPlayerItem, Error>) -> Void) {
self.playbackManager?.playerItem(forVideo: media.media, withCompletion: { (item, flag, error) in
if let error = error{
completion(.failure(error.toPlaybackError))
return
}
if let item = item{
completion(.success(item))
}
})
}

3.5 Delete a Resource File

- (void)deleteMediaItem:(AUTELMedia *_Nonnull)media
withCompletion:(AUTELCompletionBlock)block;// Deletes a resource file.

- (void)deleteMedia:(NSArray<AUTELMedia *> *_Nonnull)medias
withProgress:(AUTELFileDeletingProgress)progressBlock
deleteListCompletion:(AUTELFileDeleteListCompletion)deleteListBlock
overallCompletion:(AUTELFileDeleteCompletion)deleteAllBlock;

Call example

func delete(medias: [AUTELMedia], progress: AUTELFileDeletingProgress?, completion: @escaping (Result<[AUTELMedia], Error>) -> Void) {
playbackManager?.deleteMedia(medias, withProgress: progress, deleteListCompletion: nil, overallCompletion: { (deleteMedias, error) in
if let error = error {
completion(.failure(error.toPlaybackError))
return
}
guard let deleteMedias = deleteMedias else{
completion(.failure(ATAlbumError.delete))
return
}
let mds = medias.filter { media -> Bool in
!deleteMedias.contains(where: { $0.uniqueID == media.uniqueID })
}
completion(.success(mds))
})
}

3.6 Download a Resource File

- (void)downloadMedia:(NSArray<AUTELMedia *> *_Nonnull)medias
withPreparation:(AUTELFileDownloadPreparingBlock)prepareBlock
progress:(AUTELFileDownloadProgressBlock)progressBlock
downloadCompletion:(AUTELFileDownloadCompletionBlock)fileCompletionBlock
overallCompletion:(AUTELCompletionBlock)overallCompletionBlock;


- (void)downloadMedia:(NSArray<AUTELMedia *> *_Nonnull)medias
withType:(AUTELDownloadVideoResolutionType)resolutionType
preparation:(AUTELFileDownloadPreparingBlock)prepareBlock
progress:(AUTELFileDownloadProgressBlock)progressBlock
downloadCompletion:(AUTELFileDownloadCompletionBlock)fileCompletionBlock
overallCompletion:(AUTELCompletionBlock)overallCompletionBlock;

Call example

func downloadOriginal(medias: [ATMediaItem]) {
mediaItems = medias
let medias = mediaItems.compactMap({ $0.media })
let totalBytes = medias.reduce(0) { (result, media) -> Int64 in
return result + media.fileSizeInBytes
}
printLog("totalBytes:",totalBytes)
var downloadedCount: Int = 0
let type = ATSettings.shared.flieExporLeve
ATMediaDataManager.shared.playbackManager?.downloadMedia(medias, with: type == 0 ? AUTELDownloadVideoResolutionType.thumbnail : .original, preparation: { (media, type, skip) in
guard let media = media else { return }
self.currentMedia = media.copy() as? AUTELMedia
self.preSize = 0
self.currentSize = 0
if skip{
downloadedCount += 1
}
}, progress: {[weak self] (total, expected) in
let progress = CGFloat(total)/CGFloat(expected)
self?.progressHandler?(downloadedCount,CGFloat(progress))
self?.currentSize = total
printLog("downloadMedia progress",progress,downloadedCount + 1)
}, downloadCompletion: { (location) in
guard let media = self.currentMedia, let uniqueID = location?.lastPathComponent.components(separatedBy: ".").first else { return }
printLog("downloadCompletion",location,uniqueID)
media.setValue(uniqueID, forKey: "uniqueID")
downloadedCount += 1
self.preSize = 0
self.currentSize = 0
self.saveMedia(media,originalPath: location)
}, overallCompletion: { (error) in
logInfo("Failed to download the picture: name:\(self.currentMedia?.fileName ?? ""), err:\(error?.localizedDescription) Callback \(self.completionHandler)")
if let err = error as NSError? {
if let wifiDownload = err.userInfo["wifiDownload"] as? NSNumber,wifiDownload.boolValue == true,err.code != -999 {
if DroneConnection.shared.isConnected {
NotificationCenter.default.post(name: NSNotification.Name("WiFiDisConnectDownloadFailToRCDownload"), object: nil)
self.stopSpeedTimer()
return
}
}
}
self.completionHandler?(error?.toPlaybackError)
self.stopSpeedTimer()
})
startSpeedTimer()
}

3.7 Cancel Downloading

- (void)cancelDownloadingWithCompletion:(AUTELCompletionBlock)completion;

/** Cancels all downloading tasks, <including the tasks for downloading the thumbnail pictures or high-definition pictures> */
- (void)cancelAllDownload;

- (void)cancelAllDownloadWithCompletionBlock:(void(^)())completionBlock;

Call example

func cancelDownload(_ completionBlock:@escaping (()->())) {
if let playbackManager = playbackManager {
playbackManager.cancelDownloading(completion: { error in
completionBlock()
})
} else {
completionBlock()
}
}

4. Album Access Flow

The following takes Autel_UX_SDK demo as an example to explain how to develop album functionalities.

In this example, to facilitate functionality development, we encapsulated album APIs provided by Mobile SDK again. This mainly involves the ATMediaDataManager and ATAlbumViewController classes.

  • ATAlbumViewController: The album view controller, which is mainly used to display, download, play, or delete a file in the SD card, flash memory, or local album.
  • ATMediaDataManager: This class is used to encapsulates the APIs provided by Mobile SDK again. In this process, the local album functionalities are encapsulated at the same time.

The above classes are all encapsulated in the AutelUXFramework.framework dynamic library.

4.1 Configure the Sources of Resource Files to be Retrieved

4.1.1 Resource File Storage Type

/** Media file storage types */
public enum ATAlbumStoreType : Int {
case sdCard // The media file is stored in the SD card
case flash // The media file is stored in the flash memory
case local // The media file is stored in the local album of your phone
}

image-20220804155808272

4.1.2 Update the Resource File Type

In the demo, when switching between the SD card, flash memory, and local album, you must update the value of ATMediaDataManager.shared.storeType accordingly to ensure that the resource files from the correct sources are read.

For example, to view resource files in the SD card, you must set ATMediaDataManager.shared.storeType to ATAlbumStoreType.sdCard first (ATMediaDataManager.shared.storeType = ATAlbumStoreType.sdCard).

func switchDidChange(index: Int) {
guard let type = ATAlbumStoreType(rawValue: index) else {
return
}
ATMediaDataManager.shared.storeType = type
collectionView.mj_header?.beginRefreshing()
}

4.2 Obtain a Resource File

After you configure the resource file source (SD card, flash memory, or local album), you can start requesting album resources.

4.2.1 Enumeration Type

4.2.1.1 Resource File Type
typedef NS_ENUM(NSInteger, AUTELGetFileListType) {
/**
* @brief Image file
*
* List of all files
*/
AUTELGetFileListTypeAll = 0,

/**
* @brief Video file list
*
* Video file list
*/
AUTELGetFileListTypeVideo = 1,

/**
* @brief Picture file list
*
* Photo file list
*/
AUTELGetFileListTypePhoto = 2,
};
4.2.1.2 Mission File Type
public enum ATMediaType : Int {

/** Photo */
case photo

/** Video */
case video

/** Panoramic */
case panaromic

/** Periodic */
case burst

/** Unknown */
case unknown
}

4.2.2 Retrieve Album Files

Function name

public func getFileList(with fileListType: AUTELGetFileListType = .all, offset: Int, count: Int, handler: @escaping (Result<[AUTELMedia], Error>) -> Void)

Function instance

ATMediaDataManager.shared.getFileList(with: .all, offset: pageIndex == 0 ? 0 : allMediaItems.count, count: pageCount) {[weak self] (result) in
completion()
guard let self = self else { return }
switch result{
case let .success(medias):
self.configMediaItems(medias)
case let .failure(error):
GCDUtils.main {
self.configMediaItems([])
ToastManager.shared.show(title: error.localizedDescription)
}
}
}

Parameter description

fileListType (resource file type, see Section 2.2.1.1): AUTELGetFileListType. You can retrieve resource files of all types or just photos or videos.

offset (page number, starting from 0): Int

count (number of file items on each page. The maximum number is 50): Int

handler (callback): \<[AUTELMedia], Error>

4.2.3 Obtain the Thumbnail Picture of the First Photo in the Album

Function name

public func getFirstThumbImage(completion: @escaping (UIImage?) -> Void)

Function call example

private func setupThumbnail(){
if thumbnailImage.value != nil {
return
}
ATMediaDataManager.shared.getFirstThumbImage(completion: {[weak self] (image) in
if image != nil {
self?.thumbnailImage.accept(image)
}
})
}

4.2.4 Obtain Thumbnail Pictures of Files in the Local Album (File Path)

Function name

public func fetchThumbImagePath(media: AUTELMedia) -> String?

Function call example

func setAlbumLocalThumbImage(with media: ATMediaItem) {
GCDUtils.main {
guard let path = ATMediaDataManager.shared.fetchThumbImagePath(media: media.media) else{
return
}
self.image = UIImage(contentsOfFile: path)
}
}

4.2.5 Obtain Original Files of Photos in the Local Album (File Path)

Function name

public func fetchOriginalImagePath(media: AUTELMedia) -> String?

Function call example

func setAlbumLocalOriginalImage(with media: ATMediaItem) {
setAlbumLocalThumbImage(with: media)
guard let path = ATMediaDataManager.shared.fetchOriginalImagePath(media: media.media) else{
return
}
if media.mediaType == .video{
coverFromVideoPath(path, completion: { (image) in
GCDUtils.main {
self.image = image
}
})
return
}
GCDUtils.main {
self.image = UIImage(contentsOfFile: path)
}
}

4.2.6 Obtain the Basic Information About a Resource File

public struct ATMediaItem {

/** Resource file */
public var media: AUTELMedia

/** File type */
public var mediaType: AutelUXFramework.ATMediaType

/** File name (with a suffix) */
public var fileName: String?

/** Creation timestamp */
public var timestamp: TimeInterval

/** Video duration */
public var videoLength: String

/** Downloader for downloading thumbnail pictures */
public let lowProvider: AutelUXFramework.ATImageDataProvider

/** Downloader for downloading previews */
public let highProvider: AutelUXFramework.ATImageDataProvider

/** Whether it is selected */
public var isSelected: Bool

/** Select the index */
public var selectIndex: Int

/** Whether it is downloaded */
public var isDownloaded: Bool { get }

public init(media: AUTELMedia, dateFormatter: DateFormatter?)
/**Obtains the video cover. URL: the local path to a video file */
public func coverFromVideoPath(_ url: URL?) -> UIImage?
}

4.3 Download a Resource File

4.3.1 Enumeration Types

4.3.1.1 Resolution of the Downloaded File
public enum PhotoResolutionType : Int {
/// Low resolution
case low
/// High resolution
case high
/// Original file
case original
/// Unknown
case unknown
}

4.3.2 Download the Original File of a Single Photo

Function name

public func startDownload(media: AUTELMedia, resoType: AutelUXFramework.PhotoResolutionType, completion: @escaping (Result<Data, Error>) -> Void)

Function call example

ATMediaDataManager.shared.startDownload(media: media, resoType: .original) { result in           
}

Parameter description

media (media file): AUTELMedia

resoType (resolution, see Section 2.2.1.2): PhotoResolutionType

completion (callback): \<Data, Error>

4.3.3 Download a Small Thumbnail Picture

You can use lowProvider in ATMediaItem to download and cache thumbnail pictures to a local folder. The downloading and caching functionalities provided by lowProvider depend on a common Swift library kingfisher.

Property name

lowProvider // downloader for downloading small thumbnail pictures

Function call example

imageView.setAlbumThumbImage(with: media)
func setAlbumThumbImage(with media: ATMediaItem) {
GCDUtils.main {
self.kf.setImage(with: media.lowProvider, placeholder: self.placeholderImage)
}
}

4.3.4 Download a Large Thumbnail Picture

You can use highProvider in ATMediaItem to download and cache thumbnail pictures to a local folder. The downloading and caching functionalities provided by highProvider depend on a common Swift library kingfisher.

Property name

highProvider // downloader for downloading large thumbnail pictures

Function call example

func config(media: ATMediaItem) {
self.media = media
imageView.setAlbumHighImage(with: media)
updateLayout()
}
func setAlbumHighImage(with media: ATMediaItem) {
GCDUtils.main {
self.kf.setImage(with: media.lowProvider, placeholder: self.placeholderImage, options: nil) {[weak self] (result) in
var placeImage: UIImage?
if case let .success(image) = result{
placeImage = image.image
}
self?.kf.setImage(with: media.highProvider, placeholder: placeImage ?? self?.placeholderImage, options: nil, completionHandler: nil)
}
}
}

4.3.5 Download Multiple Photos

Function name

public func downloadOriginal(medias: [AutelUXFramework.ATMediaItem])

Function call example

func downloadItems(_ medias: [ATMediaItem]) {
ATMediaDataManager.shared.downloadOriginal(medias: medias)
showDownloadProgress(medias.count)
}

4.3.6 Cancel All Downloading Tasks

Function name

public func cancelAllDownload()// Cancels all tasks in the queue and ends the downloading task that is in progress.

Function call example

ATMediaDataManager.shared.cancelAllDownload(completionBlock)

4.3.7 Cancel the Current Downloading Task

Function name

public func cancelDownload()

Function call example

func showDownloadProgress(_ mediasCount: Int) {
let progressView = ATAlbumProgressView()

var countTitle = String(format: "ATAlbumDownloading".fLocal("ATAlbum"), 1,mediasCount)
var speedTitle = ": 0K/s" + "\n\n" + "ATAlbumDownloadSpeedLow".fLocal("ATAlbum")

let pop = ATPopup<ATAlert>.build.theme(.white).title(countTitle + speedTitle)
.addConfirmButton(title: "ATAlbumCancel".fLocal("ATAlbum")) { (alert) in
alert.hide(animiated: true)
ATMediaDataManager.shared.cancelDownload()
}.addCustomView(progressView).done()
pop.show(animiated: true)

progressView.snp.remakeConstraints { (make) in
make.edges.equalToSuperview()
make.width.height.equalTo(55.fit)
}

let downloader = ATMediaDataManager.shared.downloader
downloader.progressHandler = {[weak progressView, weak pop] count,progress in
GCDUtils.main {
progressView?.update(progress)
countTitle = String(format: "ATAlbumDownloading".fLocal("ATAlbum"), count + 1,mediasCount)
pop?.viewModel.title = countTitle + speedTitle
pop?.update()
}

}
downloader.speedHandler = {[weak pop] speed in
GCDUtils.main {
speedTitle = ": " + speed + "\n\n" + "ATAlbumDownloadSpeedLow".fLocal("ATAlbum")
pop?.viewModel.title = countTitle + speedTitle
pop?.update()
}
}
downloader.completionHandler = { [weak self,weak pop] error in
GCDUtils.main {
pop?.hide(animiated: true)
if error != nil{
self?.showSuccessAlert(false)
}else{
self?.showFailedAlert(false)
}
}
}
}

4.4 Delete a Resource File

4.4.1 Delete a Resource File

Function name

public func delete(medias: [AUTELMedia], progress: AUTELFileDeletingProgress?, completion: @escaping (Result<[AUTELMedia], Error>) -> Void)

Function call example

func deleteItems(_ medias: [AUTELMedia]) {
var progressHandler: AUTELFileDeletingProgress?
var completionHandler: (() -> Void)?
showDeleteProgress(medias.count, progressHandler: &progressHandler, completionHandler: &completionHandler)
ATMediaDataManager.shared.delete(medias: medias, progress: progressHandler) { result in
GCDUtils.main {
completionHandler?()
switch result{
case let .success(medias):
self.removeItems(medias: medias)
self.showSuccessAlert()
case .failure(_):
self.showFailedAlert()
}
}
}
}

4.4.2 Cancel All Deletion Tasks

Function name

public func cancelDelete()// Cancels all deletion tasks.

Function call example

ATMediaDataManager.shared.cancelDelete()

4.5 Play a Resource File

4.5.1 Obtain a Video File

Function name

public func fetchPlayerItem(media: AUTELMedia, completion: @escaping (Result<AVPlayerItem, Error>) -> Void)

Function call example

func play(_ playerView: ATPlayerView) {
guard let media = media else {
return
}
if let media = media as? ATMediaItem, let localPath = ATMediaDataManager.shared.fetchLocalPath(media: media, media.videoPathType) {// If a video file exists and the file is stored in a local path
let item = AVPlayerItem(url: URL(fileURLWithPath: localPath))
playerView.setPlayerItem(item)
playerView.player?.autoReplay = false
}else {// Obtains the video file path through this method
loadingIndicatorView.startAnimating()
media.fetchPlayerItem {[weak playerView] result in
GCDUtils.main {
self.loadingIndicatorView.stopAnimating()
switch result{
case let .success(item):
playerView?.setPlayerItem(item)
playerView?.player?.autoReplay = false
playerView?.player?.uniqueID = media.uniqueID
playerView?.player?.play()
case .failure(_):
// ToastManager.shared.show(title: error.localizedDescription)
break
}
}
}
}
playerView.player?.uniqueID = media.uniqueID
contentView.addSubview(playerView)
playerView.snp.remakeConstraints { (make) in
make.edges.equalTo(imageView)
}
playerView.player?.play()
self.playerView = playerView
contentView.layoutIfNeeded()
}

4.6 Download the Management Module

4.6.1 Obtain the Downloading Management Module

Example for calling the property

let downloader = ATMediaDataManager.shared.downloader

After you obtain the downloading management module through let downloader = ATMediaDataManager.shared.downloader, you can use progressHandler, speedHandler, and completionHandler to display the downloading details on the UI.

func showDownloadProgress(_ mediasCount: Int) {
let progressView = ATAlbumProgressView()

var countTitle = String(format: "ATAlbumDownloading".fLocal("ATAlbum"), 1,mediasCount)
var speedTitle = ": 0K/s" + "\n\n" + "ATAlbumDownloadSpeedLow".fLocal("ATAlbum")

let pop = ATPopup<ATAlert>.build.theme(.white).title(countTitle + speedTitle)
.addConfirmButton(title: "ATAlbumCancel".fLocal("ATAlbum")) { (alert) in
alert.hide(animiated: true)
ATMediaDataManager.shared.cancelDownload()
}.addCustomView(progressView).done()
pop.show(animiated: true)

progressView.snp.remakeConstraints { (make) in
make.edges.equalToSuperview()
make.width.height.equalTo(55.fit)
}

let downloader = ATMediaDataManager.shared.downloader
downloader.progressHandler = {[weak progressView, weak pop] count,progress in
GCDUtils.main {
progressView?.update(progress)
countTitle = String(format: "ATAlbumDownloading".fLocal("ATAlbum"), count + 1,mediasCount)
pop?.viewModel.title = countTitle + speedTitle
pop?.update()
}

}
downloader.speedHandler = {[weak pop] speed in
GCDUtils.main {
speedTitle = ": " + speed + "\n\n" + "ATAlbumDownloadSpeedLow".fLocal("ATAlbum")
pop?.viewModel.title = countTitle + speedTitle
pop?.update()
}
}
downloader.completionHandler = { [weak self,weak pop] error in
GCDUtils.main {
pop?.hide(animiated: true)
if error != nil{
self?.showSuccessAlert(false)
}else{
self?.showFailedAlert(false)
}
}
}
}

4.6.2 Display the Downloading Progress

Obtain the downloading manager through let downloader = ATMediaDataManager.shared.downloader and then obtain the downloading speed, downloading progress, and task completion information.

Function call example

func showDownloadProgress(_ mediasCount: Int) {
let progressView = ATAlbumProgressView()

var countTitle = String(format: "ATAlbumDownloading".fLocal("ATAlbum"), 1,mediasCount)
var speedTitle = ": 0K/s" + "\n\n" + "ATAlbumDownloadSpeedLow".fLocal("ATAlbum")

let pop = ATPopup<ATAlert>.build.theme(.white).title(countTitle + speedTitle)
.addConfirmButton(title: "ATAlbumCancel".fLocal("ATAlbum")) { (alert) in
alert.hide(animiated: true)
ATMediaDataManager.shared.cancelDownload()
}.addCustomView(progressView).done()
pop.show(animiated: true)

progressView.snp.remakeConstraints { (make) in
make.edges.equalToSuperview()
make.width.height.equalTo(55.fit)
}

let downloader = ATMediaDataManager.shared.downloader
downloader.progressHandler = {[weak progressView, weak pop] count,progress in
GCDUtils.main {
progressView?.update(progress)
countTitle = String(format: "ATAlbumDownloading".fLocal("ATAlbum"), count + 1,mediasCount)
pop?.viewModel.title = countTitle + speedTitle
pop?.update()
}

}
downloader.speedHandler = {[weak pop] speed in
GCDUtils.main {
speedTitle = ": " + speed + "\n\n" + "ATAlbumDownloadSpeedLow".fLocal("ATAlbum")
pop?.viewModel.title = countTitle + speedTitle
pop?.update()
}
}
downloader.completionHandler = { [weak self,weak pop] error in
GCDUtils.main {
pop?.hide(animiated: true)
if error != nil{
self?.showSuccessAlert(false)
}else{
self?.showFailedAlert(false)
}
}
}
}