diff --git a/Example/ContentView.swift b/Example/ContentView.swift index d1f6869..32fe973 100644 --- a/Example/ContentView.swift +++ b/Example/ContentView.swift @@ -12,8 +12,8 @@ import AVKit struct ContentView: View { var body: some View { VStack { - let player = TPAVPlayer(assetID: "peBmzxeQ7Mf", - accessToken: "d7ebb4b2-8dee-4dff-bb00-e833195b0756") + let player = TPAVPlayer(assetID: "8eaHZjXt6km", + accessToken: "16b608ba-9979-45a0-94fb-b27c1a86b3c1") TPStreamPlayerView(player: player) .frame(height: 240) Spacer() diff --git a/Source/Database/ObjectManager.swift b/Source/Database/ObjectManager.swift index 4d98d03..2ae93c7 100644 --- a/Source/Database/ObjectManager.swift +++ b/Source/Database/ObjectManager.swift @@ -28,7 +28,7 @@ public class ObjectManager { } } } - + func exists(id: Any) -> Bool { let object = realm.object(ofType: T.self, forPrimaryKey: id) return object != nil diff --git a/Source/Database/TPStreamsDownloadManager.swift b/Source/Database/TPStreamsDownloadManager.swift index 2e47678..52c6c75 100644 --- a/Source/Database/TPStreamsDownloadManager.swift +++ b/Source/Database/TPStreamsDownloadManager.swift @@ -82,7 +82,7 @@ internal class AssetDownloadDelegate: NSObject, AVAssetDownloadDelegate { let percentageComplete = calculateDownloadPercentage(loadedTimeRanges, timeRangeExpectedToLoad) OfflineAsset.manager.update(object: offlineAsset, with: ["status": Status.inProgress.rawValue, "percentageCompleted": percentageComplete]) } - + private func updateDownloadCompleteStatus(_ error: Error?,_ offlineAsset: OfflineAsset) { let status: Status = (error == nil) ? .finished : .failed let updateValues: [String: Any] = ["status": status.rawValue, "downloadedAt": Date()] diff --git a/Source/Managers/TPStreamPlayer.swift b/Source/Managers/TPStreamPlayer.swift index 701d528..c3297c2 100644 --- a/Source/Managers/TPStreamPlayer.swift +++ b/Source/Managers/TPStreamPlayer.swift @@ -26,6 +26,9 @@ class TPStreamPlayer: NSObject { var currentVideoQuality: VideoQuality? { return self.player.availableVideoQualities.first( where: {$0.bitrate == self.player.currentItem?.preferredPeakBitRate }) } + var asset: Asset? { + return self.player.asset + } init(player: TPAVPlayer){ self.player = player diff --git a/Source/TPStreamPlayerConfiguration.swift b/Source/TPStreamPlayerConfiguration.swift index 93639d2..99f510b 100644 --- a/Source/TPStreamPlayerConfiguration.swift +++ b/Source/TPStreamPlayerConfiguration.swift @@ -13,6 +13,7 @@ public struct TPStreamPlayerConfiguration { public var preferredRewindDuration: TimeInterval = 10.0 public var watchedProgressTrackColor: UIColor = .red public var progressBarThumbColor: UIColor = .red + public var showDownloadOption: Bool = false } @@ -43,6 +44,11 @@ public class TPStreamPlayerConfigurationBuilder { return self } + public func showDownloadOption() -> Self { + configuration.showDownloadOption = true + return self + } + public func build() -> TPStreamPlayerConfiguration { return configuration } diff --git a/Source/Views/SwiftUI/PlayerSettingsButton.swift b/Source/Views/SwiftUI/PlayerSettingsButton.swift index 3210e53..2d247a3 100644 --- a/Source/Views/SwiftUI/PlayerSettingsButton.swift +++ b/Source/Views/SwiftUI/PlayerSettingsButton.swift @@ -29,7 +29,7 @@ struct PlayerSettingsButton: View { return ActionSheet( title: Text("Settings"), message: nil, - buttons: [playbackSpeedButton(), videoQualityButton(), .cancel()] + buttons: [playbackSpeedButton(), videoQualityButton(), downloadQualityButton(), .cancel()] ) case .playbackSpeed: return ActionSheet( @@ -43,6 +43,12 @@ struct PlayerSettingsButton: View { message: nil, buttons: videoQualityOptions() + [.cancel()] ) + case .downloadQuality: + return ActionSheet( + title: Text("Download Quality"), + message: nil, + buttons: downloadQualityOptions() + [.cancel()] + ) } } @@ -64,6 +70,15 @@ struct PlayerSettingsButton: View { } } + private func downloadQualityButton() -> ActionSheet.Button { + return .default(Text("Download")) { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + self.showOptions = true + self.currentMenu = .downloadQuality + } + } + } + private func playbackSpeedOptions() -> [ActionSheet.Button] { let playbackSpeeds = PlaybackSpeed.allCases return playbackSpeeds.map { speed in @@ -80,6 +95,17 @@ struct PlayerSettingsButton: View { } } } + + private func downloadQualityOptions() -> [ActionSheet.Button] { + var availableVideoQualities = player.availableVideoQualities + // Remove Auto Quality from the Array + availableVideoQualities.remove(at: 0) + return availableVideoQualities.map { downloadQuality in + .default(Text(downloadQuality.resolution)) { + TPStreamsDownloadManager.shared.startDownload(asset: player.asset!, videoQuality: downloadQuality) + } + } + } } -enum SettingsMenu { case main, playbackSpeed, videoQuality } +enum SettingsMenu { case main, playbackSpeed, videoQuality, downloadQuality } diff --git a/Source/Views/UIKit/PlayerControlsUIView.swift b/Source/Views/UIKit/PlayerControlsUIView.swift index 4c932ba..618f6ca 100644 --- a/Source/Views/UIKit/PlayerControlsUIView.swift +++ b/Source/Views/UIKit/PlayerControlsUIView.swift @@ -119,9 +119,12 @@ class PlayerControlsUIView: UIView { optionsMenu.addAction(UIAlertAction(title: "Playback Speed", style: .default) { _ in self.showPlaybackSpeedMenu()}) optionsMenu.addAction(UIAlertAction(title: "Video Quality", style: .default, handler: { action in self.showVideoQualityMenu()})) optionsMenu.addAction(UIAlertAction(title: "Cancel", style: .cancel)) + if playerConfig.showDownloadOption { + optionsMenu.addAction(UIAlertAction(title: "Download", style: .default, handler: { action in self.showDownloadQualityMenu()})) + } parentViewController?.present(optionsMenu, animated: true, completion: nil) } - + func showPlaybackSpeedMenu(){ let playbackSpeedMenu = createPlaybackSpeedMenu() parentViewController?.present(playbackSpeedMenu, animated: true, completion: nil) @@ -132,14 +135,19 @@ class PlayerControlsUIView: UIView { parentViewController?.present(videoQualityMenu, animated: true, completion: nil) } + func showDownloadQualityMenu(){ + let downloadQualityMenu = createDownloadQualityMenu() + parentViewController?.present(downloadQualityMenu, animated: true, completion: nil) + } + func createPlaybackSpeedMenu() -> UIAlertController { let playbackSpeedMenu = UIAlertController(title: "Playback Speed", message: nil, preferredStyle: ACTION_SHEET_PREFERRED_STYLE) - + for playbackSpeed in PlaybackSpeed.allCases { let action = createActionForPlaybackSpeed(playbackSpeed) playbackSpeedMenu.addAction(action) } - + playbackSpeedMenu.addAction(UIAlertAction(title: "Cancel", style: .cancel)) return playbackSpeedMenu } @@ -153,12 +161,25 @@ class PlayerControlsUIView: UIView { qualityMenu.addAction(UIAlertAction(title: "Cancel", style: .cancel)) return qualityMenu } - + + func createDownloadQualityMenu() -> UIAlertController { + let qualityMenu = UIAlertController(title: "Available resolutions", message: nil, preferredStyle: ACTION_SHEET_PREFERRED_STYLE) + var availableVideoQualities = player.availableVideoQualities + // Remove Auto Quality from the Array + availableVideoQualities.remove(at: 0) + for quality in availableVideoQualities { + let action = createActionForDownload(quality) + qualityMenu.addAction(action) + } + qualityMenu.addAction(UIAlertAction(title: "Cancel", style: .cancel)) + return qualityMenu + } + func createActionForPlaybackSpeed(_ playbackSpeed: PlaybackSpeed) -> UIAlertAction { let action = UIAlertAction(title: playbackSpeed.label, style: .default) { [weak self] _ in self?.player.changePlaybackSpeed(playbackSpeed) } - + if playbackSpeed == .normal && self.player.currentPlaybackSpeed.rawValue == 0.0 || (playbackSpeed.rawValue == self.player.currentPlaybackSpeed.rawValue) { action.setValue(UIImage(named: "checkmark", in: bundle, compatibleWith: nil), forKey: "image") } @@ -176,7 +197,15 @@ class PlayerControlsUIView: UIView { return action } - + + func createActionForDownload(_ quality: VideoQuality) -> UIAlertAction { + let action = UIAlertAction(title: quality.resolution, style: .default, handler: { (_) in + TPStreamsDownloadManager.shared.startDownload(asset: self.player.asset!, videoQuality: quality) + }) + + return action + } + @IBAction func toggleFullScreen(_ sender: Any) { if isFullScreen { fullScreenToggleDelegate?.exitFullScreen() diff --git a/StoryboardExample/ViewController.swift b/StoryboardExample/ViewController.swift index 3fc7097..b39c451 100644 --- a/StoryboardExample/ViewController.swift +++ b/StoryboardExample/ViewController.swift @@ -40,6 +40,7 @@ class ViewController: UIViewController { .setPreferredRewindDuration(5) .setprogressBarThumbColor(.systemBlue) .setwatchedProgressTrackColor(.systemBlue) + .showDownloadOption() .build() playerViewController?.config = config