diff --git a/Source/Database/TPStreamsDownloadManager.swift b/Source/Database/TPStreamsDownloadManager.swift index f5315d0..dcbc20e 100644 --- a/Source/Database/TPStreamsDownloadManager.swift +++ b/Source/Database/TPStreamsDownloadManager.swift @@ -95,6 +95,7 @@ public final class TPStreamsDownloadManager { assetID: String, accessToken: String? = nil, resolution: String? = nil, + allowResolutionFallback: Bool = false, metadata: [String: Any]? = nil, presentingViewController: UIViewController? = nil, completion: ((Result) -> Void)? = nil @@ -125,7 +126,13 @@ public final class TPStreamsDownloadManager { completion?(.failure(error)) case .success(let (qualities, playlistModel)): if let requestedResolution = resolution { - guard let quality = qualities.first(where: { $0.resolution == requestedResolution }) else { + let selectedQuality = VideoQualityUtils.selectClosestQuality( + in: qualities, + for: requestedResolution, + allowFallback: allowResolutionFallback + ) + + guard let quality = selectedQuality else { completion?(.failure(.resolutionNotAvailable(requestedResolution))) return } diff --git a/Source/Utils/VideoQualityUtils.swift b/Source/Utils/VideoQualityUtils.swift new file mode 100644 index 0000000..5bde6fa --- /dev/null +++ b/Source/Utils/VideoQualityUtils.swift @@ -0,0 +1,45 @@ +// +// VideoQualityUtils.swift +// TPStreamsSDK +// +// Created by Prithuvi on 05/03/26. +// + +import Foundation + +extension VideoQuality { + var resolutionHeight: Int? { + var height: Int = 0 + return Scanner(string: resolution).scanInt(&height) ? height : nil + } +} + +public class VideoQualityUtils { + public static func selectClosestQuality( + in qualities: [VideoQuality], + for resolution: String, + allowFallback: Bool + ) -> VideoQuality? { + if let exactMatch = qualities.first(where: { $0.resolution == resolution }) { + return exactMatch + } + + var requestedHeight: Int = 0 + guard allowFallback, Scanner(string: resolution).scanInt(&requestedHeight) else { + return nil + } + + return qualities.compactMap { quality -> (quality: VideoQuality, height: Int)? in + guard let height = quality.resolutionHeight else { return nil } + return (quality, height) + }.min { first, second in + let diff1 = abs(first.height - requestedHeight) + let diff2 = abs(second.height - requestedHeight) + + if diff1 == diff2 { + return first.height < second.height + } + return diff1 < diff2 + }?.quality + } +} diff --git a/StoryboardExample/MainViewController.swift b/StoryboardExample/MainViewController.swift index bc0e890..17168e8 100644 --- a/StoryboardExample/MainViewController.swift +++ b/StoryboardExample/MainViewController.swift @@ -25,7 +25,8 @@ class MainViewController: UIViewController { TPStreamsDownloadManager.shared.startDownload( assetID: "BEArYFdaFbt", accessToken: "ecf6366b-c2ee-408c-9472-6ed4e4b3047e", - // resolution: "720p", + resolution: "140p", + allowResolutionFallback: true, presentingViewController: self, ) { result in switch result { diff --git a/iOSPlayerSDK.xcodeproj/project.pbxproj b/iOSPlayerSDK.xcodeproj/project.pbxproj index 6d284c1..6ad96e6 100644 --- a/iOSPlayerSDK.xcodeproj/project.pbxproj +++ b/iOSPlayerSDK.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 03CE75022A7A337B00B84304 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03CE75012A7A337B00B84304 /* UIView.swift */; }; 03D7F4252C21C64D00DF3597 /* LiveIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D7F4242C21C64D00DF3597 /* LiveIndicatorView.swift */; }; 03D7F4282C22C4F900DF3597 /* PlaybackSpeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03D7F4272C22C4F900DF3597 /* PlaybackSpeed.swift */; }; + 2D3CE81F2F5957EE0011CAB7 /* VideoQualityUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D3CE81E2F5957EE0011CAB7 /* VideoQualityUtils.swift */; }; 2D46EC6A2EE856BB008B559A /* M3U8Kit in Frameworks */ = {isa = PBXBuildFile; productRef = 2D46EC692EE856BB008B559A /* M3U8Kit */; }; 2DD12A212D4CF75400272433 /* RealmSwift in Embed Frameworks */ = {isa = PBXBuildFile; productRef = D92E48F22CB0226A00B1FAC3 /* RealmSwift */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 70D42E0B2E6075F3002AC32C /* AnyRealmValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70D42E0A2E6075F3002AC32C /* AnyRealmValue.swift */; }; @@ -201,6 +202,7 @@ 03CE75012A7A337B00B84304 /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; 03D7F4242C21C64D00DF3597 /* LiveIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveIndicatorView.swift; sourceTree = ""; }; 03D7F4272C22C4F900DF3597 /* PlaybackSpeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackSpeed.swift; sourceTree = ""; }; + 2D3CE81E2F5957EE0011CAB7 /* VideoQualityUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoQualityUtils.swift; sourceTree = ""; }; 70D42E0A2E6075F3002AC32C /* AnyRealmValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyRealmValue.swift; sourceTree = ""; }; 8E6389BB2A2724D000306FA4 /* TPStreamPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TPStreamPlayerView.swift; sourceTree = ""; }; 8E6389C12A27277D00306FA4 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -373,6 +375,7 @@ isa = PBXGroup; children = ( 03CE74FF2A7946A800B84304 /* Time.swift */, + 2D3CE81E2F5957EE0011CAB7 /* VideoQualityUtils.swift */, 0367F6F42B861CFC0000922D /* Sentry.swift */, D960A8FA2CEDEE7C003B0B04 /* M3U8Parser.swift */, ); @@ -799,6 +802,7 @@ 03CA2D372A30A8E500532549 /* PlayerProgressBar.swift in Sources */, 0374E3782C1B26EC00CE9CF2 /* NoticeView.swift in Sources */, 0377C40E2A2B1C7300F7E58F /* BaseAPI.swift in Sources */, + 2D3CE81F2F5957EE0011CAB7 /* VideoQualityUtils.swift in Sources */, 03CA2D312A2F757D00532549 /* TimeIndicatorView.swift in Sources */, 035351A22A2F49E3001E38F3 /* MediaControlsView.swift in Sources */, D92E48B62CAFBD6F00B1FAC3 /* ObjectManager.swift in Sources */,