From 25b1f1cba3af4e837441fb5dff178fd8bbc532ab Mon Sep 17 00:00:00 2001 From: PruthiviRaj27 Date: Thu, 25 Jan 2024 16:14:33 +0530 Subject: [PATCH 1/4] add download list view --- Example/Views/ContentView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Example/Views/ContentView.swift b/Example/Views/ContentView.swift index a0e818b..3ad52c1 100644 --- a/Example/Views/ContentView.swift +++ b/Example/Views/ContentView.swift @@ -26,7 +26,7 @@ struct ContentView: View { .navigationBarTitle("Sample App") } } - + private func nonDRMNavigationLink(title: String, assetId: String, accessToken: String) -> some View { NavigationLink(destination: PlayerView(title: title, assetId: assetId, accessToken: accessToken)) { Text(title) @@ -37,7 +37,7 @@ struct ContentView: View { .cornerRadius(10) } } - + private func downloadListNavigationLink() -> some View { NavigationLink(destination: DownloadListView()) { Text("Download List") From 71b15e9bef119be960aa3dc102a302ad2f3e0c1b Mon Sep 17 00:00:00 2001 From: PruthiviRaj27 Date: Thu, 25 Jan 2024 16:31:04 +0530 Subject: [PATCH 2/4] add download listener --- Example/AppDownloadManager.swift | 56 +++++++++++++++++++ Example/Views/DownloadListView.swift | 11 ++-- .../Database/TPStreamsDownloadManager.swift | 21 +++++++ iOSPlayerSDK.xcodeproj/project.pbxproj | 4 ++ 4 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 Example/AppDownloadManager.swift diff --git a/Example/AppDownloadManager.swift b/Example/AppDownloadManager.swift new file mode 100644 index 0000000..db89de6 --- /dev/null +++ b/Example/AppDownloadManager.swift @@ -0,0 +1,56 @@ +// +// 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 onDownloadComplete(offlineAsset: OfflineAsset) { + print("Download Complete", offlineAsset.assetId) + } + + func onDownloadStart(offlineAsset: OfflineAsset) { + print("Download Start", offlineAsset.assetId) + } + + func onDownloadPause(offlineAsset: OfflineAsset) { + print("Download Pause", offlineAsset.assetId) + } + + func onDownloadResume(offlineAsset: OfflineAsset) { + print("Download Resume", offlineAsset.assetId) + } + + @objc func handleOfflineAssetsUpdated() { + getOfflineAssets() + print("Offline Assets Updated") + } + + func onDownloadsStateChange() { + print("Downloads State Change") + getOfflineAssets() + } + + deinit { + NotificationCenter.default.removeObserver(self, name: Notification.Name("OfflineAssetsUpdated"), object: nil) + } + +} diff --git a/Example/Views/DownloadListView.swift b/Example/Views/DownloadListView.swift index 48c7451..b6d1c78 100644 --- a/Example/Views/DownloadListView.swift +++ b/Example/Views/DownloadListView.swift @@ -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") } } diff --git a/Source/Database/TPStreamsDownloadManager.swift b/Source/Database/TPStreamsDownloadManager.swift index 9f652af..2a90a1b 100644 --- a/Source/Database/TPStreamsDownloadManager.swift +++ b/Source/Database/TPStreamsDownloadManager.swift @@ -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") @@ -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) { @@ -50,12 +56,14 @@ public final class TPStreamsDownloadManager { OfflineAsset.manager.add(object: offlineAsset) assetDownloadDelegate.activeDownloadsMap[task] = offlineAsset task.resume() + tpStreamsDownloadDelegate?.onDownloadStart(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?.onDownloadPause(offlineAsset: offlineAsset) } } @@ -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?.onDownloadResume(offlineAsset: offlineAsset) } } } @@ -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?.onDownloadComplete(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?.onDownloadsStateChange() } func urlSession(_ session: URLSession, @@ -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?.onDownloadsStateChange() } private func updateDownloadCompleteStatus(_ error: Error?,_ offlineAsset: OfflineAsset) { @@ -118,3 +131,11 @@ internal class AssetDownloadDelegate: NSObject, AVAssetDownloadDelegate { return percentageComplete * 100 } } + +public protocol TPStreamsDownloadDelegate { + func onDownloadComplete(offlineAsset: OfflineAsset) + func onDownloadStart(offlineAsset: OfflineAsset) + func onDownloadPause(offlineAsset: OfflineAsset) + func onDownloadResume(offlineAsset: OfflineAsset) + func onDownloadsStateChange() +} diff --git a/iOSPlayerSDK.xcodeproj/project.pbxproj b/iOSPlayerSDK.xcodeproj/project.pbxproj index 6fcdd34..9c96390 100644 --- a/iOSPlayerSDK.xcodeproj/project.pbxproj +++ b/iOSPlayerSDK.xcodeproj/project.pbxproj @@ -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 */ @@ -185,6 +186,7 @@ D9D6A3B02B62717B005390D7 /* OfflineAssetRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfflineAssetRow.swift; sourceTree = ""; }; D9D6A3B22B6271BB005390D7 /* PlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerView.swift; sourceTree = ""; }; D9D6A3B72B628B0F005390D7 /* Time.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Time.swift; sourceTree = ""; }; + D9D6A3B42B6274E4005390D7 /* AppDownloadManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDownloadManager.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -317,6 +319,7 @@ 8E6389C32A27277D00306FA4 /* ExampleApp.swift */, 8E6389C72A27278000306FA4 /* Assets.xcassets */, 8E6389C92A27278000306FA4 /* Preview Content */, + D9D6A3B42B6274E4005390D7 /* AppDownloadManager.swift */, ); path = Example; sourceTree = ""; @@ -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 */, ); From ca3e80d5cd6652ebc71659a6e81d61418f67ad99 Mon Sep 17 00:00:00 2001 From: PruthiviRaj27 Date: Thu, 25 Jan 2024 18:22:06 +0530 Subject: [PATCH 3/4] refcator --- Example/AppDownloadManager.swift | 12 +++++----- .../Database/TPStreamsDownloadManager.swift | 22 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Example/AppDownloadManager.swift b/Example/AppDownloadManager.swift index db89de6..6ff3d68 100644 --- a/Example/AppDownloadManager.swift +++ b/Example/AppDownloadManager.swift @@ -23,19 +23,19 @@ class AppDownloadManager: TPStreamsDownloadDelegate, ObservableObject { offlineAssets = TPStreamsDownloadManager.shared.getAllOfflineAssets() } - func onDownloadComplete(offlineAsset: OfflineAsset) { - print("Download Complete", offlineAsset.assetId) + func onComplete(offlineAsset: OfflineAsset) { + print("Download Complete", offlineAsset) } - func onDownloadStart(offlineAsset: OfflineAsset) { + func onStart(offlineAsset: OfflineAsset) { print("Download Start", offlineAsset.assetId) } - func onDownloadPause(offlineAsset: OfflineAsset) { + func onPause(offlineAsset: OfflineAsset) { print("Download Pause", offlineAsset.assetId) } - func onDownloadResume(offlineAsset: OfflineAsset) { + func onResume(offlineAsset: OfflineAsset) { print("Download Resume", offlineAsset.assetId) } @@ -44,7 +44,7 @@ class AppDownloadManager: TPStreamsDownloadDelegate, ObservableObject { print("Offline Assets Updated") } - func onDownloadsStateChange() { + func onStateChange(offlineAsset: OfflineAsset) { print("Downloads State Change") getOfflineAssets() } diff --git a/Source/Database/TPStreamsDownloadManager.swift b/Source/Database/TPStreamsDownloadManager.swift index 2a90a1b..bbc2533 100644 --- a/Source/Database/TPStreamsDownloadManager.swift +++ b/Source/Database/TPStreamsDownloadManager.swift @@ -56,14 +56,14 @@ public final class TPStreamsDownloadManager { OfflineAsset.manager.add(object: offlineAsset) assetDownloadDelegate.activeDownloadsMap[task] = offlineAsset task.resume() - tpStreamsDownloadDelegate?.onDownloadStart(offlineAsset: offlineAsset) + 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?.onDownloadPause(offlineAsset: offlineAsset) + tpStreamsDownloadDelegate?.onPause(offlineAsset: offlineAsset) } } @@ -72,7 +72,7 @@ public final class TPStreamsDownloadManager { if task.state != .running { task.resume() OfflineAsset.manager.update(object: offlineAsset, with: ["status": Status.inProgress.rawValue]) - tpStreamsDownloadDelegate?.onDownloadResume(offlineAsset: offlineAsset) + tpStreamsDownloadDelegate?.onResume(offlineAsset: offlineAsset) } } } @@ -93,13 +93,13 @@ internal class AssetDownloadDelegate: NSObject, AVAssetDownloadDelegate { guard let offlineAsset = activeDownloadsMap[assetDownloadTask] else { return } updateDownloadCompleteStatus(error, offlineAsset) activeDownloadsMap.removeValue(forKey: assetDownloadTask) - tpStreamsDownloadDelegate?.onDownloadComplete(offlineAsset: offlineAsset) + 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?.onDownloadsStateChange() + tpStreamsDownloadDelegate?.onStateChange(offlineAsset: offlineAsset) } func urlSession(_ session: URLSession, @@ -113,7 +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?.onDownloadsStateChange() + tpStreamsDownloadDelegate?.onStateChange(offlineAsset: offlineAsset) } private func updateDownloadCompleteStatus(_ error: Error?,_ offlineAsset: OfflineAsset) { @@ -133,9 +133,9 @@ internal class AssetDownloadDelegate: NSObject, AVAssetDownloadDelegate { } public protocol TPStreamsDownloadDelegate { - func onDownloadComplete(offlineAsset: OfflineAsset) - func onDownloadStart(offlineAsset: OfflineAsset) - func onDownloadPause(offlineAsset: OfflineAsset) - func onDownloadResume(offlineAsset: OfflineAsset) - func onDownloadsStateChange() + func onComplete(offlineAsset: OfflineAsset) + func onStart(offlineAsset: OfflineAsset) + func onPause(offlineAsset: OfflineAsset) + func onResume(offlineAsset: OfflineAsset) + func onStateChange(offlineAsset: OfflineAsset) } From 1a31b44b122753c9396fba31bcabe50f74b1fb75 Mon Sep 17 00:00:00 2001 From: PruthiviRaj27 Date: Wed, 31 Jan 2024 11:04:29 +0530 Subject: [PATCH 4/4] recfactor --- Example/AppDownloadManager.swift | 1 + Example/Views/ContentView.swift | 4 ++-- Example/Views/OfflineAssetRow.swift | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Example/AppDownloadManager.swift b/Example/AppDownloadManager.swift index 6ff3d68..129a818 100644 --- a/Example/AppDownloadManager.swift +++ b/Example/AppDownloadManager.swift @@ -24,6 +24,7 @@ class AppDownloadManager: TPStreamsDownloadDelegate, ObservableObject { } func onComplete(offlineAsset: OfflineAsset) { + getOfflineAssets() print("Download Complete", offlineAsset) } diff --git a/Example/Views/ContentView.swift b/Example/Views/ContentView.swift index 3ad52c1..a0e818b 100644 --- a/Example/Views/ContentView.swift +++ b/Example/Views/ContentView.swift @@ -26,7 +26,7 @@ struct ContentView: View { .navigationBarTitle("Sample App") } } - + private func nonDRMNavigationLink(title: String, assetId: String, accessToken: String) -> some View { NavigationLink(destination: PlayerView(title: title, assetId: assetId, accessToken: accessToken)) { Text(title) @@ -37,7 +37,7 @@ struct ContentView: View { .cornerRadius(10) } } - + private func downloadListNavigationLink() -> some View { NavigationLink(destination: DownloadListView()) { Text("Download List") diff --git a/Example/Views/OfflineAssetRow.swift b/Example/Views/OfflineAssetRow.swift index f308853..1af4984 100644 --- a/Example/Views/OfflineAssetRow.swift +++ b/Example/Views/OfflineAssetRow.swift @@ -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) } }