Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions Example/AppDownloadManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// AppDownloadManager.swift
// Example
//
// Created by Prithuvi on 25/01/24.
//

import Foundation
import TPStreamsSDK

class AppDownloadManager: TPStreamsDownloadDelegate, ObservableObject {

@Published var offlineAssets: [OfflineAsset] = []

init() {
TPStreamsDownloadManager.shared.setTPStreamsDownloadDelegate(tpStreamsDownloadDelegate: self)
getOfflineAssets()

NotificationCenter.default.addObserver(self, selector: #selector(handleOfflineAssetsUpdated), name: Notification.Name("OfflineAssetsUpdated"), object: nil)
}

func getOfflineAssets() {
offlineAssets = TPStreamsDownloadManager.shared.getAllOfflineAssets()
}

func onComplete(offlineAsset: OfflineAsset) {
getOfflineAssets()
print("Download Complete", offlineAsset)
}

func onStart(offlineAsset: OfflineAsset) {
print("Download Start", offlineAsset.assetId)
}

func onPause(offlineAsset: OfflineAsset) {
print("Download Pause", offlineAsset.assetId)
}

func onResume(offlineAsset: OfflineAsset) {
print("Download Resume", offlineAsset.assetId)
}

@objc func handleOfflineAssetsUpdated() {
getOfflineAssets()
print("Offline Assets Updated")
}

func onStateChange(offlineAsset: OfflineAsset) {
print("Downloads State Change")
getOfflineAssets()
}

deinit {
NotificationCenter.default.removeObserver(self, name: Notification.Name("OfflineAssetsUpdated"), object: nil)
}

}
11 changes: 4 additions & 7 deletions Example/Views/DownloadListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,15 @@ import SwiftUI
import TPStreamsSDK

struct DownloadListView: View {
@State private var offlineAssets: [OfflineAsset] = []
@ObservedObject private var appDownloadManager = AppDownloadManager()

var body: some View {
List($offlineAssets, id: \.self) { offlineAsset in
List($appDownloadManager.offlineAssets, id: \.self) { offlineAsset in
OfflineAssetRow(offlineAsset: offlineAsset)
}
.onAppear {
offlineAssets = TPStreamsDownloadManager.shared.getAllOfflineAssets()
}
.overlay(
Group {
if offlineAssets.isEmpty {
if appDownloadManager.offlineAssets.isEmpty {
Text("No downloads available")
}
}
Expand Down
2 changes: 1 addition & 1 deletion Example/Views/OfflineAssetRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct OfflineAssetRow: View {

ProgressView(value: offlineAsset.percentageCompleted, total: 100)

Text("\(formatDate(date: offlineAsset.createdAt)) • \(formatDuration(seconds: offlineAsset.duration)) • \(String(format: "%.2f MB", offlineAsset.size / 8 / 1024 / 1024))")
Text("\(formatDate(date: offlineAsset.createdAt)) • \(formatDuration(seconds: offlineAsset.duration)) • \(String(format: "%.2f MB", offlineAsset.size / 8 / 1024 / 1024)) • \(offlineAsset.status)")
.font(.caption)
}
}
Expand Down
21 changes: 21 additions & 0 deletions Source/Database/TPStreamsDownloadManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public final class TPStreamsDownloadManager {
static public let shared = TPStreamsDownloadManager()
private var assetDownloadURLSession: AVAssetDownloadURLSession!
private var assetDownloadDelegate: AssetDownloadDelegate!
private var tpStreamsDownloadDelegate: TPStreamsDownloadDelegate? = nil

private init() {
let backgroundConfiguration = URLSessionConfiguration.background(withIdentifier: "com.tpstreams.downloadSession")
Expand All @@ -24,6 +25,11 @@ public final class TPStreamsDownloadManager {
delegateQueue: OperationQueue.main
)
}

public func setTPStreamsDownloadDelegate(tpStreamsDownloadDelegate: TPStreamsDownloadDelegate) {
self.tpStreamsDownloadDelegate = tpStreamsDownloadDelegate
assetDownloadDelegate.tpStreamsDownloadDelegate = tpStreamsDownloadDelegate
}

internal func startDownload(asset: Asset, videoQuality: VideoQuality) {

Expand All @@ -50,12 +56,14 @@ public final class TPStreamsDownloadManager {
OfflineAsset.manager.add(object: offlineAsset)
assetDownloadDelegate.activeDownloadsMap[task] = offlineAsset
task.resume()
tpStreamsDownloadDelegate?.onStart(offlineAsset: offlineAsset)
}

public func pauseDownload(_ offlineAsset: OfflineAsset) {
if let task = assetDownloadDelegate.activeDownloadsMap.first(where: { $0.value == offlineAsset })?.key {
task.suspend()
OfflineAsset.manager.update(object: offlineAsset, with: ["status": Status.paused.rawValue])
tpStreamsDownloadDelegate?.onPause(offlineAsset: offlineAsset)
}
}

Expand All @@ -64,6 +72,7 @@ public final class TPStreamsDownloadManager {
if task.state != .running {
task.resume()
OfflineAsset.manager.update(object: offlineAsset, with: ["status": Status.inProgress.rawValue])
tpStreamsDownloadDelegate?.onResume(offlineAsset: offlineAsset)
}
}
}
Expand All @@ -77,17 +86,20 @@ public final class TPStreamsDownloadManager {
internal class AssetDownloadDelegate: NSObject, AVAssetDownloadDelegate {

var activeDownloadsMap = [AVAggregateAssetDownloadTask: OfflineAsset]()
var tpStreamsDownloadDelegate: TPStreamsDownloadDelegate? = nil

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
guard let assetDownloadTask = task as? AVAggregateAssetDownloadTask else { return }
guard let offlineAsset = activeDownloadsMap[assetDownloadTask] else { return }
updateDownloadCompleteStatus(error, offlineAsset)
activeDownloadsMap.removeValue(forKey: assetDownloadTask)
tpStreamsDownloadDelegate?.onComplete(offlineAsset: offlineAsset)
}

func urlSession(_ session: URLSession, aggregateAssetDownloadTask: AVAggregateAssetDownloadTask, willDownloadTo location: URL) {
guard let offlineAsset = activeDownloadsMap[aggregateAssetDownloadTask] else { return }
OfflineAsset.manager.update(object: offlineAsset, with: ["downloadedPath": String(location.relativePath)])
tpStreamsDownloadDelegate?.onStateChange(offlineAsset: offlineAsset)
}

func urlSession(_ session: URLSession,
Expand All @@ -101,6 +113,7 @@ internal class AssetDownloadDelegate: NSObject, AVAssetDownloadDelegate {

let percentageComplete = calculateDownloadPercentage(loadedTimeRanges, timeRangeExpectedToLoad)
OfflineAsset.manager.update(object: offlineAsset, with: ["status": Status.inProgress.rawValue, "percentageCompleted": percentageComplete])
tpStreamsDownloadDelegate?.onStateChange(offlineAsset: offlineAsset)
}

private func updateDownloadCompleteStatus(_ error: Error?,_ offlineAsset: OfflineAsset) {
Expand All @@ -118,3 +131,11 @@ internal class AssetDownloadDelegate: NSObject, AVAssetDownloadDelegate {
return percentageComplete * 100
}
}

public protocol TPStreamsDownloadDelegate {
func onComplete(offlineAsset: OfflineAsset)
func onStart(offlineAsset: OfflineAsset)
func onPause(offlineAsset: OfflineAsset)
func onResume(offlineAsset: OfflineAsset)
func onStateChange(offlineAsset: OfflineAsset)
}
4 changes: 4 additions & 0 deletions iOSPlayerSDK.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
D9D6A3B12B62717B005390D7 /* OfflineAssetRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9D6A3B02B62717B005390D7 /* OfflineAssetRow.swift */; };
D9D6A3B32B6271BB005390D7 /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9D6A3B22B6271BB005390D7 /* PlayerView.swift */; };
D9D6A3B82B628B0F005390D7 /* Time.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9D6A3B72B628B0F005390D7 /* Time.swift */; };
D9D6A3B52B6274E4005390D7 /* AppDownloadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9D6A3B42B6274E4005390D7 /* AppDownloadManager.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -185,6 +186,7 @@
D9D6A3B02B62717B005390D7 /* OfflineAssetRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfflineAssetRow.swift; sourceTree = "<group>"; };
D9D6A3B22B6271BB005390D7 /* PlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerView.swift; sourceTree = "<group>"; };
D9D6A3B72B628B0F005390D7 /* Time.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Time.swift; sourceTree = "<group>"; };
D9D6A3B42B6274E4005390D7 /* AppDownloadManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDownloadManager.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -317,6 +319,7 @@
8E6389C32A27277D00306FA4 /* ExampleApp.swift */,
8E6389C72A27278000306FA4 /* Assets.xcassets */,
8E6389C92A27278000306FA4 /* Preview Content */,
D9D6A3B42B6274E4005390D7 /* AppDownloadManager.swift */,
);
path = Example;
sourceTree = "<group>";
Expand Down Expand Up @@ -657,6 +660,7 @@
8E6389C62A27277D00306FA4 /* ContentView.swift in Sources */,
8E6389C42A27277D00306FA4 /* ExampleApp.swift in Sources */,
D9D6A3AF2B627144005390D7 /* DownloadListView.swift in Sources */,
D9D6A3B52B6274E4005390D7 /* AppDownloadManager.swift in Sources */,
D9D6A3B32B6271BB005390D7 /* PlayerView.swift in Sources */,
D9D6A3B12B62717B005390D7 /* OfflineAssetRow.swift in Sources */,
);
Expand Down