import AVFoundation import Promises struct RCTVideoDRM { @available(*, unavailable) private init() {} static func fetchLicense( licenseServer: String, spcData: Data?, contentId: String, headers: [String:Any]? ) -> Promise { let request = createLicenseRequest(licenseServer:licenseServer, spcData:spcData, contentId:contentId, headers:headers) return Promise(on: .global()) { fulfill, reject in let postDataTask = URLSession.shared.dataTask(with: request as URLRequest, completionHandler:{ (data:Data!,response:URLResponse!,error:Error!) in let httpResponse:HTTPURLResponse! = (response as! HTTPURLResponse) guard error == nil else { print("Error getting license from \(licenseServer), HTTP status code \(httpResponse.statusCode)") reject(error) return } guard httpResponse.statusCode == 200 else { print("Error getting license from \(licenseServer), HTTP status code \(httpResponse.statusCode)") reject(RCTVideoErrorHandler.licenseRequestNotOk(httpResponse.statusCode)) return } guard data != nil, let decodedData = Data(base64Encoded: data, options: []) else { reject(RCTVideoErrorHandler.noDataFromLicenseRequest) return } fulfill(decodedData) }) postDataTask.resume() } } static func createLicenseRequest( licenseServer: String, spcData: Data?, contentId: String, headers: [String:Any]? ) -> URLRequest { var request = URLRequest(url: URL(string: licenseServer)!) request.httpMethod = "POST" if let headers = headers { for item in headers { guard let key = item.key as? String, let value = item.value as? String else { continue } request.setValue(value, forHTTPHeaderField: key) } } let spcEncoded = spcData?.base64EncodedString(options: []) let spcUrlEncoded = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, spcEncoded as? CFString? as! CFString, nil, "?=&+" as CFString, CFStringBuiltInEncodings.UTF8.rawValue) as? String let post = String(format:"spc=%@&%@", spcUrlEncoded as! CVarArg, contentId) let postData = post.data(using: String.Encoding.utf8, allowLossyConversion:true) request.httpBody = postData return request } static func fetchSpcData( loadingRequest: AVAssetResourceLoadingRequest, certificateData: Data, contentIdData: Data ) -> Promise { return Promise(on: .global()) { fulfill, reject in var spcError:NSError! var spcData: Data? do { spcData = try loadingRequest.streamingContentKeyRequestData(forApp: certificateData, contentIdentifier: contentIdData as Data, options: nil) } catch _ { print("SPC error") } if spcError != nil { reject(spcError) } guard let spcData = spcData else { reject(RCTVideoErrorHandler.noSPC) return } fulfill(spcData) } } static func createCertificateData(certificateStringUrl:String?, base64Certificate:Bool?) -> Promise { return Promise(on: .global()) { fulfill, reject in guard let certificateStringUrl = certificateStringUrl, let certificateURL = URL(string: certificateStringUrl.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) ?? "") else { reject(RCTVideoErrorHandler.noCertificateURL) return } var certificateData:Data? do { certificateData = try Data(contentsOf: certificateURL) if (base64Certificate != nil) { certificateData = Data(base64Encoded: certificateData! as Data, options: .ignoreUnknownCharacters) } } catch {} guard let certificateData = certificateData else { reject(RCTVideoErrorHandler.noCertificateData) return } fulfill(certificateData) } } static func handleWithOnGetLicense(loadingRequest: AVAssetResourceLoadingRequest, contentId:String?, certificateUrl:String?, base64Certificate:Bool?) -> Promise { let contentIdData = contentId?.data(using: .utf8) return RCTVideoDRM.createCertificateData(certificateStringUrl:certificateUrl, base64Certificate:base64Certificate) .then{ certificateData -> Promise in guard let contentIdData = contentIdData else { throw RCTVideoError.invalidContentId as! Error } return RCTVideoDRM.fetchSpcData( loadingRequest:loadingRequest, certificateData:certificateData, contentIdData:contentIdData ) } } static func handleInternalGetLicense(loadingRequest: AVAssetResourceLoadingRequest, contentId:String?, licenseServer:String?, certificateUrl:String?, base64Certificate:Bool?, headers: [String:Any]?) -> Promise { let url = loadingRequest.request.url guard let contentId = contentId ?? url?.absoluteString.replacingOccurrences(of: "skd://", with:"") else { return Promise(RCTVideoError.invalidContentId as! Error) } let contentIdData = NSData(bytes: contentId.cString(using: String.Encoding.utf8), length:contentId.lengthOfBytes(using: String.Encoding.utf8)) as Data return RCTVideoDRM.createCertificateData(certificateStringUrl:certificateUrl, base64Certificate:base64Certificate) .then{ certificateData in return RCTVideoDRM.fetchSpcData( loadingRequest:loadingRequest, certificateData:certificateData, contentIdData:contentIdData ) } .then{ spcData -> Promise in guard let licenseServer = licenseServer else { throw RCTVideoError.noLicenseServerURL as! Error } return RCTVideoDRM.fetchLicense( licenseServer: licenseServer, spcData: spcData, contentId: contentId, headers: headers ) } } }