diff --git a/CHANGELOG.md b/CHANGELOG.md index c7bfaaa2b..931a7712b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [11.2.0] - 26-06-04 + +### Added + +- Added support for `CustomAdIntegrationKind`, allowing custom ad integrations on top of the player API. +- Added support for `breakManifestUrl` for OptiView Ads streams. + ## [11.1.0] - 26-05-27 ### Added diff --git a/android/build.gradle b/android/build.gradle index 23139c788..88c71ab9e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -126,8 +126,8 @@ repositories { mavenLocal() } -// The minimum supported THEOplayer version is 11.0.0 -def theoVersion = safeExtGet('THEOplayer_sdk', '[11.0.0, 12.0.0)') +// The minimum supported THEOplayer version is 11.4.0 +def theoVersion = safeExtGet('THEOplayer_sdk', '[11.4.0, 12.0.0)') def theoMediaSessionVersion = safeExtGet('THEOplayer_mediasession', '[11.0.0, 12.0.0)') def theoAdsWrapperVersion = "11.0.0" def coroutinesVersion = safeExtGet('coroutinesVersion', '1.10.2') diff --git a/android/src/main/java/com/theoplayer/ads/AdsModule.kt b/android/src/main/java/com/theoplayer/ads/AdsModule.kt index fa47a941f..afe0dd30f 100644 --- a/android/src/main/java/com/theoplayer/ads/AdsModule.kt +++ b/android/src/main/java/com/theoplayer/ads/AdsModule.kt @@ -45,7 +45,7 @@ class AdsModule(context: ReactApplicationContext) : ReactContextBaseJavaModule(c fun schedule(tag: Int, ad: ReadableMap) { viewResolver.resolveViewByTag(tag) { view: ReactTHEOplayerView? -> try { - view?.adsApi?.schedule(sourceHelper.parseAdFromJS(ad)) + view?.adsApi?.schedule(sourceHelper.parseAdDescriptionFromJS(ad)) } catch (exception: THEOplayerException) { Log.e(NAME, exception.message ?: ERR_SCHEDULE_AD) } diff --git a/android/src/main/java/com/theoplayer/source/SourceAdapter.kt b/android/src/main/java/com/theoplayer/source/SourceAdapter.kt index d01403bca..264a1dd3f 100644 --- a/android/src/main/java/com/theoplayer/source/SourceAdapter.kt +++ b/android/src/main/java/com/theoplayer/source/SourceAdapter.kt @@ -23,6 +23,7 @@ import com.theoplayer.android.api.ads.theoads.TheoAdsLayoutOverride import com.theoplayer.android.api.cmcd.CMCDTransmissionMode import com.theoplayer.android.api.error.ErrorCode import com.theoplayer.android.api.source.AdIntegration +import com.theoplayer.android.api.source.addescription.CustomAdDescriptionRegistry import com.theoplayer.android.api.source.dash.DashPlaybackConfiguration import com.theoplayer.android.api.theolive.PlayoutDelay import com.theoplayer.android.api.theolive.TheoLiveSource @@ -73,6 +74,7 @@ private const val PROP_RETRIEVE_POD_ID_URI = "retrievePodIdURI" private const val PROP_INITIALIZATION_DELAY = "initializationDelay" private const val PROP_SSE_ENDPOINT = "sseEndpoint" private const val PROP_STREAM_ACTIVITY_MONITOR_ID = "streamActivityMonitorId" +private const val PROP_BREAK_MANIFEST_URL = "breakManifestUrl" private const val PROP_LATENCY_CONFIGURATION = "latencyConfiguration" private const val PROP_PROFILE = "profile" private const val PROP_WEBRTC: String = "webrtc" @@ -82,8 +84,8 @@ private const val PROP_PLAYOUT_DELAY_MAX: String = "maximum" private const val ERROR_IMA_NOT_ENABLED = "Google IMA support not enabled." private const val ERROR_THEOADS_NOT_ENABLED = "THEOads support not enabled." -private const val ERROR_UNSUPPORTED_CSAI_INTEGRATION = "Unsupported CSAI integration" -private const val ERROR_MISSING_CSAI_INTEGRATION = "Missing CSAI integration" +private const val ERROR_UNSUPPORTED_AD_INTEGRATION = "Unsupported Ad integration" +private const val ERROR_MISSING_AD_INTEGRATION = "Missing Ad integration" private const val PROP_SSAI_INTEGRATION_GOOGLE_DAI = "google-dai" @@ -151,7 +153,9 @@ class SourceAdapter { val jsonAdDescription = jsonAds[i] as JSONObject // Currently only ima-ads are supported. - ads.add(parseAdFromJS(jsonAdDescription)) + parseAdDescriptionFromJS(jsonAdDescription)?.let { + ads.add(it) + } } } @@ -278,16 +282,6 @@ class SourceAdapter { } } - @Throws(THEOplayerException::class) - fun parseAdFromJS(map: ReadableMap): AdDescription? { - return try { - parseAdFromJS(JSONObject(gson.toJson(map.toHashMap()))) - } catch (e: JSONException) { - e.printStackTrace() - null - } - } - private fun parseSourceType(jsonTypedSource: JSONObject): SourceType? { val type = jsonTypedSource.optString(PROP_TYPE) if (type.isNotEmpty()) { @@ -308,30 +302,39 @@ class SourceAdapter { return null } + @Throws(THEOplayerException::class) + fun parseAdDescriptionFromJS(map: ReadableMap): AdDescription? { + return try { + parseAdDescriptionFromJS(JSONObject(gson.toJson(map.toHashMap()))) + } catch (e: JSONException) { + e.printStackTrace() + null + } + } + @Throws(JSONException::class, THEOplayerException::class) - fun parseAdFromJS(jsonAdDescription: JSONObject): AdDescription { + fun parseAdDescriptionFromJS(jsonAdDescription: JSONObject): AdDescription? { val integrationStr = jsonAdDescription.optString(PROP_INTEGRATION) return if (!TextUtils.isEmpty(integrationStr)) { when (integrationStr) { - AdIntegration.GOOGLE_IMA.adIntegration -> parseImaAdFromJS(jsonAdDescription) - AdIntegration.THEO_ADS.adIntegration -> parseTheoAdFromJS(jsonAdDescription) - else -> { - throw THEOplayerException( + AdIntegration.GOOGLE_IMA.adIntegration -> parseImaAdDescriptionFromJS(jsonAdDescription) + AdIntegration.THEO_ADS.adIntegration -> parseTheoAdDescriptionFromJS(jsonAdDescription) + else -> + CustomAdDescriptionRegistry.deserialize(integrationStr, jsonAdDescription.toString()) ?: throw THEOplayerException( ErrorCode.AD_ERROR, - "$ERROR_UNSUPPORTED_CSAI_INTEGRATION: $integrationStr" + "$ERROR_UNSUPPORTED_AD_INTEGRATION: $integrationStr" ) - } } } else { throw THEOplayerException( ErrorCode.AD_ERROR, - "$ERROR_MISSING_CSAI_INTEGRATION: $integrationStr" + "$ERROR_MISSING_AD_INTEGRATION: $integrationStr" ) } } @Throws(THEOplayerException::class) - private fun parseImaAdFromJS(jsonAdDescription: JSONObject): GoogleImaAdDescription { + private fun parseImaAdDescriptionFromJS(jsonAdDescription: JSONObject): GoogleImaAdDescription { if (!BuildConfig.EXTENSION_GOOGLE_IMA) { throw THEOplayerException(ErrorCode.AD_ERROR, ERROR_IMA_NOT_ENABLED) } @@ -349,7 +352,7 @@ class SourceAdapter { } @Throws(JSONException::class) - private fun parseTheoAdFromJS(jsonAdDescription: JSONObject): TheoAdDescription { + private fun parseTheoAdDescriptionFromJS(jsonAdDescription: JSONObject): TheoAdDescription { if (!BuildConfig.EXTENSION_THEOADS) { throw THEOplayerException(ErrorCode.AD_ERROR, ERROR_THEOADS_NOT_ENABLED) } @@ -366,6 +369,7 @@ class SourceAdapter { initializationDelay = jsonAdDescription.optDouble(PROP_INITIALIZATION_DELAY).takeIf { it.isFinite() }, sseEndpoint = jsonAdDescription.optString(PROP_SSE_ENDPOINT).takeIf { it.isNotEmpty() }, streamActivityMonitorId = jsonAdDescription.optString(PROP_STREAM_ACTIVITY_MONITOR_ID).takeIf { it.isNotEmpty() }, + breakManifestUrl = jsonAdDescription.optString(PROP_BREAK_MANIFEST_URL).takeIf { it.isNotEmpty() }, ) } @@ -376,9 +380,9 @@ class SourceAdapter { private fun parseOverrideLayout(override: String?): TheoAdsLayoutOverride? { return when (override) { - "single", "single-if-mobile" -> return TheoAdsLayoutOverride.SINGLE - "l-shape" -> return TheoAdsLayoutOverride.LSHAPE - "double" -> return TheoAdsLayoutOverride.DOUBLE + "single", "single-if-mobile" -> TheoAdsLayoutOverride.SINGLE + "l-shape" -> TheoAdsLayoutOverride.LSHAPE + "double" -> TheoAdsLayoutOverride.DOUBLE else -> null } } @@ -395,11 +399,11 @@ class SourceAdapter { private fun parseTextTrackKind(kind: String?): TextTrackKind? { return when (kind) { - "subtitles" -> return TextTrackKind.SUBTITLES - "metadata" -> return TextTrackKind.METADATA - "captions" -> return TextTrackKind.CAPTIONS - "chapters" -> return TextTrackKind.CHAPTERS - "descriptions" -> return TextTrackKind.DESCRIPTIONS + "subtitles" -> TextTrackKind.SUBTITLES + "metadata" -> TextTrackKind.METADATA + "captions" -> TextTrackKind.CAPTIONS + "chapters" -> TextTrackKind.CHAPTERS + "descriptions" -> TextTrackKind.DESCRIPTIONS else -> null } } diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 07a360e15..1ceb0d882 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -53,7 +53,7 @@ } }, "..": { - "version": "10.13.0", + "version": "11.1.0", "license": "BSD-3-Clause-Clear", "dependencies": { "@theoplayer/cmcd-connector-web": "^1.5.0", @@ -74,7 +74,7 @@ "react": "^19.2.3", "react-native": "^0.84.1", "react-native-builder-bob": "^0.39.1", - "theoplayer": "^11.0.0", + "theoplayer": "^11.4.0", "typedoc": "^0.25.13", "typedoc-plugin-external-resolver": "^1.0.3", "typedoc-plugin-mdn-links": "^3.3.4", @@ -87,7 +87,7 @@ "peerDependencies": { "react": "*", "react-native": "*", - "theoplayer": "^11" + "theoplayer": "^11.4.0" }, "peerDependenciesMeta": { "theoplayer": { diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 9e25e3493..6de786415 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -46,7 +46,7 @@ hermesEnabled=true edgeToEdgeEnabled=false # Version of the THEOplayer SDK, if not specified, the latest available version within bounds is set. -#THEOplayer_sdk=[11.0.0, 12.0.0) +#THEOplayer_sdk=[11.4.0, 12.0.0) # Override Android sdk versions #THEOplayer_compileSdkVersion = 36 diff --git a/example/package-lock.json b/example/package-lock.json index a14c8be66..ca77249d6 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -40,7 +40,7 @@ "eslint": "^8.57.1", "html-webpack-plugin": "^5.6.3", "react-native-svg-web": "^1.0.9", - "theoplayer": "^11", + "theoplayer": "^11.4.0", "typescript": "5.8.3", "webpack": "^5.105.0", "webpack-cli": "^6.0.1", @@ -51,7 +51,7 @@ } }, "..": { - "version": "11.0.0", + "version": "11.1.0", "license": "BSD-3-Clause-Clear", "dependencies": { "@theoplayer/cmcd-connector-web": "^1.5.0", @@ -72,7 +72,7 @@ "react": "^19.2.3", "react-native": "^0.84.1", "react-native-builder-bob": "^0.39.1", - "theoplayer": "^11.0.0", + "theoplayer": "^11.4.0", "typedoc": "^0.25.13", "typedoc-plugin-external-resolver": "^1.0.3", "typedoc-plugin-mdn-links": "^3.3.4", @@ -85,7 +85,7 @@ "peerDependencies": { "react": "*", "react-native": "*", - "theoplayer": "^11" + "theoplayer": "^11.4.0" }, "peerDependenciesMeta": { "theoplayer": { @@ -13396,9 +13396,9 @@ "license": "MIT" }, "node_modules/theoplayer": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/theoplayer/-/theoplayer-11.0.0.tgz", - "integrity": "sha512-pxjpNtd++m87H5ndEPlNbKpHhXCNbFDgnXB0g6RfTv/v7zSpSLwiB0F0vIJe5asaqEJ6pKH7kvpTgnaEN2TElw==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/theoplayer/-/theoplayer-11.4.0.tgz", + "integrity": "sha512-LJEXxDOW41NWM62dyj/4coc1a5jksriFXTGCuGVV+x31DGiAffHdhUBJo7So7wsvMzsoBTzuZZF6Ei/FsIFYJA==", "license": "SEE LICENSE AT https://www.theoplayer.com/terms" }, "node_modules/thingies": { @@ -13697,7 +13697,7 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/example/package.json b/example/package.json index 487e0d103..ab97f0529 100644 --- a/example/package.json +++ b/example/package.json @@ -47,7 +47,7 @@ "eslint": "^8.57.1", "html-webpack-plugin": "^5.6.3", "react-native-svg-web": "^1.0.9", - "theoplayer": "^11", + "theoplayer": "^11.4.0", "typescript": "5.8.3", "webpack": "^5.105.0", "webpack-cli": "^6.0.1", diff --git a/ios/THEOplayerRCTPlayerAPI.swift b/ios/THEOplayerRCTPlayerAPI.swift index 3269a2120..27a2f3e8c 100644 --- a/ios/THEOplayerRCTPlayerAPI.swift +++ b/ios/THEOplayerRCTPlayerAPI.swift @@ -356,8 +356,14 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule { let quality = foundTrack.qualities.get(index) return uids.contains { $0.intValue == quality.bandwidth } ? quality : nil } - foundTrack.targetQualities = matchingQualities - if DEBUG_PLAYER_API { PrintUtils.printLog(logText: "[NATIVE] targetQualities: \(uids) set on active videotrack.") } + foundTrack.targetQualities = matchingQualities.count > 0 ? matchingQualities : nil + if DEBUG_PLAYER_API { + if matchingQualities.count > 0 { + if DEBUG_PLAYER_API { PrintUtils.printLog(logText: "[NATIVE] targetQualities: \(uids) set on active videotrack. (matching: \(matchingQualities.map(\.bandwidth)))") } + } else { + if DEBUG_PLAYER_API { PrintUtils.printLog(logText: "[NATIVE] targetQualities: \(uids) set on active videotrack. (no match or empty) => no quality restriction.") } + } + } } } } diff --git a/ios/THEOplayerRCTSourceDescriptionBuilder.swift b/ios/THEOplayerRCTSourceDescriptionBuilder.swift index 177ec9231..edd6e50f9 100644 --- a/ios/THEOplayerRCTSourceDescriptionBuilder.swift +++ b/ios/THEOplayerRCTSourceDescriptionBuilder.swift @@ -62,6 +62,7 @@ let SD_PROP_USE_ID3: String = "useId3" let SD_PROP_RETRIEVE_POD_ID_URI: String = "retrievePodIdURI" let SD_PROP_INITIALIZATION_DELAY: String = "initializationDelay" let SD_PROP_HLS_DATE_RANGE: String = "hlsDateRange" +let SD_PROP_BREAK_MANIFEST_URL: String = "breakManifestUrl" let SD_PROP_CMCD: String = "cmcd" let SD_PROP_QUERY_PARAMETERS: String = "queryParameters" @@ -90,7 +91,7 @@ class THEOplayerRCTSourceDescriptionBuilder { guard let sourcesData = sourceData[SD_PROP_SOURCES] else { return (nil, nil) } - + #if os(iOS) if let metadataData = sourceData[SD_PROP_METADATA] as? [String:Any], let cachingTaskId = metadataData[SD_PROP_METADATA_CACHINGTASK_ID] as? String { @@ -164,7 +165,7 @@ class THEOplayerRCTSourceDescriptionBuilder { if let metadataData = sourceData[SD_PROP_METADATA] as? [String:Any] { metadataDescription = THEOplayerRCTSourceDescriptionBuilder.buildMetaDataDescription(metadataData) } - + // 6. configure CMCD let cmcd = sourceData[SD_PROP_CMCD] as? [String:Any] if cmcd != nil { @@ -179,7 +180,7 @@ class THEOplayerRCTSourceDescriptionBuilder { ads: adsDescriptions, poster: poster, metadata: metadataDescription) - + return (sourceDescription, metadataAndChapterTrackDescriptions) } @@ -193,7 +194,7 @@ class THEOplayerRCTSourceDescriptionBuilder { let contentProtection = extractDrmConfiguration(from: typedSourceData) let integration = typedSourceData[SD_PROP_INTEGRATION] as? String let type = typedSourceData[SD_PROP_TYPE] as? String - + if integration == "theolive" || type == "theolive" { return THEOplayerRCTSourceDescriptionBuilder.buildTHEOliveDescription(typedSourceData, contentProtection: contentProtection) } @@ -247,7 +248,7 @@ class THEOplayerRCTSourceDescriptionBuilder { let textTrackFormat = THEOplayerRCTSourceDescriptionBuilder.extractTextTrackFormat(textTracksData[SD_PROP_FORMAT] as? String) let textTrackPTS = textTracksData[SD_PROP_PTS] as? String let textTrackLocalTime = textTracksData[SD_PROP_LOCALTIME] as? String ?? "00:00:00.000" - + #if canImport(THEOplayerConnectorSideloadedSubtitle) let ttDescription = SSTextTrackDescription(src: textTrackSrc, srclang: textTrackSrcLang, @@ -345,7 +346,7 @@ class THEOplayerRCTSourceDescriptionBuilder { */ static func buildContentProtection(_ contentProtectionData: [String:Any]) -> MultiplatformDRMConfiguration? { let customIntegrationId = contentProtectionData[SD_PROP_INTEGRATION] as? String ?? "internal" - + // fairplay var fairplayKeySystem: THEOplayerSDK.KeySystemConfiguration? = nil if let fairplayData = contentProtectionData[SD_PROP_FAIRPLAY] as? [String:Any] { @@ -363,7 +364,7 @@ class THEOplayerRCTSourceDescriptionBuilder { queryParameters[key] = "\(value)" } } - + fairplayKeySystem = KeySystemConfiguration( licenseAcquisitionURL: licenseAcquisitionURL, certificateURL: certificateUrl, @@ -372,7 +373,7 @@ class THEOplayerRCTSourceDescriptionBuilder { queryParameters: queryParameters ) } - + // widevine var widevineKeySystem: THEOplayerSDK.KeySystemConfiguration? = nil if let widevineData = contentProtectionData[SD_PROP_WIDEVINE] as? [String:Any] { @@ -390,7 +391,7 @@ class THEOplayerRCTSourceDescriptionBuilder { queryParameters[key] = "\(value)" } } - + widevineKeySystem = KeySystemConfiguration( licenseAcquisitionURL: licenseAcquisitionURL, certificateURL: certificateUrl, @@ -399,7 +400,7 @@ class THEOplayerRCTSourceDescriptionBuilder { queryParameters: queryParameters ) } - + // global query parameters var queryParameters: [String: String] = [:] if let allQueryParams = contentProtectionData[SD_PROP_QUERY_PARAMETERS] as? [String:Any] { @@ -408,7 +409,7 @@ class THEOplayerRCTSourceDescriptionBuilder { } } let integrationParameters = contentProtectionData[SD_PROP_INTEGRATION_PARAMETERS] as? [String:Any] ?? [:] - + return MultiplatformDRMConfiguration( customIntegrationId: customIntegrationId, integrationParameters: integrationParameters, diff --git a/ios/ads/THEOplayerRCTSourceDescriptionBuilder+Ads.swift b/ios/ads/THEOplayerRCTSourceDescriptionBuilder+Ads.swift index d1ceb89c2..820f1e59b 100644 --- a/ios/ads/THEOplayerRCTSourceDescriptionBuilder+Ads.swift +++ b/ios/ads/THEOplayerRCTSourceDescriptionBuilder+Ads.swift @@ -6,6 +6,17 @@ import UIKit let SD_PROP_ADS: String = "ads" +public class CustomAdDescription: AdDescription { + public let integration: THEOplayerSDK.AdIntegration? = AdIntegration.none + public let customIntegrationId: String + public let integrationData: [String: Any] + + public init(customIntegrationId: String, integrationData: [String: Any] = [:]) { + self.customIntegrationId = customIntegrationId + self.integrationData = integrationData + } +} + extension THEOplayerRCTSourceDescriptionBuilder { /** @@ -24,11 +35,16 @@ extension THEOplayerRCTSourceDescriptionBuilder { adsDescriptions?.append(adDescription) } else { if DEBUG_SOURCE_DESCRIPTION_BUILDER { - PrintUtils.printLog(logText: "[NATIVE] Could not create THEOplayer GoogleImaAdDescription from adsData array") + PrintUtils.printLog(logText: "[NATIVE] Could not create AdDescription from adsData") } - return nil } } + if adsDescriptions?.isEmpty == true { + if DEBUG_SOURCE_DESCRIPTION_BUILDER { + PrintUtils.printLog(logText: "[NATIVE] Could not create any AdDescription from adsData array") + } + return nil + } } // case: single ads object else if let adsData = ads as? [String:Any] { @@ -36,7 +52,7 @@ extension THEOplayerRCTSourceDescriptionBuilder { adsDescriptions?.append(adDescription) } else { if DEBUG_SOURCE_DESCRIPTION_BUILDER { - PrintUtils.printLog(logText: "[NATIVE] Could not create THEOplayer GoogleImaAdDescription from adsData") + PrintUtils.printLog(logText: "[NATIVE] Could not create AdDescription from adsData") } return nil } @@ -58,11 +74,22 @@ extension THEOplayerRCTSourceDescriptionBuilder { case "theoads": return THEOplayerRCTSourceDescriptionBuilder.buildSingleTHEOadsDescription(adsData) default: - if DEBUG_SOURCE_DESCRIPTION_BUILDER { PrintUtils.printLog(logText: "[NATIVE] We currently require and only support the 'google-ima' or 'sgai' integration in the 'ads' description.") } + return THEOplayerRCTSourceDescriptionBuilder.buildSingleCustomAdsDescription(adsData) } } return nil } + + /** + Creates a THEOplayer AdDescription, containing all passed properties. + - returns: a THEOplayer AdDescription + */ + static func buildSingleCustomAdsDescription(_ adsData: [String:Any]) -> AdDescription? { + if let integration = adsData[SD_PROP_INTEGRATION] as? String { + return CustomAdDescription(customIntegrationId: integration, integrationData: adsData) + } + return nil + } /** Creates a THEOplayer GoogleImaAdDescription. This requires an ads property in the RN source description. diff --git a/ios/theoAds/THEOplayerRCTSourceDescriptionBuilder+TheoAds.swift b/ios/theoAds/THEOplayerRCTSourceDescriptionBuilder+TheoAds.swift index a9e003004..a469c4119 100644 --- a/ios/theoAds/THEOplayerRCTSourceDescriptionBuilder+TheoAds.swift +++ b/ios/theoAds/THEOplayerRCTSourceDescriptionBuilder+TheoAds.swift @@ -50,7 +50,11 @@ extension THEOplayerRCTSourceDescriptionBuilder { let streamActivityMonitorId = adsData[SD_PROP_STREAM_ACTIVITY_MONITOR_ID_THEOADS] as? String let retrievePodIdURI = adsData[SD_PROP_RETRIEVE_POD_ID_URI] as? String let initializationDelay = adsData[SD_PROP_INITIALIZATION_DELAY] as? Double - + var breakManifestUrl: URL? + if let breakManifestUrlString = adsData[SD_PROP_BREAK_MANIFEST_URL] as? String { + breakManifestUrl = URL(string: breakManifestUrlString) + } + return THEOAdDescription(networkCode: networkCode, customAssetKey: customAssetKey, backdropDoubleBox: backdropDoubleBox, @@ -62,7 +66,8 @@ extension THEOplayerRCTSourceDescriptionBuilder { streamActivityMonitorId: streamActivityMonitorId, sseEndpoint: sseEndpoint, retrievePodIdURI: retrievePodIdURI, - initializationDelay: initializationDelay) + initializationDelay: initializationDelay, + breakManifestUrl: breakManifestUrl) #else return nil #endif diff --git a/package-lock.json b/package-lock.json index e7e27ba61..d5a005e7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "react-native-theoplayer", - "version": "11.1.0", + "version": "11.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "react-native-theoplayer", - "version": "11.1.0", + "version": "11.2.0", "license": "BSD-3-Clause-Clear", "dependencies": { "@theoplayer/cmcd-connector-web": "^1.5.0", @@ -27,7 +27,7 @@ "react": "^19.2.3", "react-native": "^0.84.1", "react-native-builder-bob": "^0.39.1", - "theoplayer": "^11.0.0", + "theoplayer": "^11.4.0", "typedoc": "^0.25.13", "typedoc-plugin-external-resolver": "^1.0.3", "typedoc-plugin-mdn-links": "^3.3.4", @@ -40,7 +40,7 @@ "peerDependencies": { "react": "*", "react-native": "*", - "theoplayer": "^11" + "theoplayer": "^11.4.0" }, "peerDependenciesMeta": { "theoplayer": { @@ -10937,9 +10937,9 @@ "dev": true }, "node_modules/theoplayer": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/theoplayer/-/theoplayer-11.0.0.tgz", - "integrity": "sha512-pxjpNtd++m87H5ndEPlNbKpHhXCNbFDgnXB0g6RfTv/v7zSpSLwiB0F0vIJe5asaqEJ6pKH7kvpTgnaEN2TElw==", + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/theoplayer/-/theoplayer-11.4.0.tgz", + "integrity": "sha512-LJEXxDOW41NWM62dyj/4coc1a5jksriFXTGCuGVV+x31DGiAffHdhUBJo7So7wsvMzsoBTzuZZF6Ei/FsIFYJA==", "license": "SEE LICENSE AT https://www.theoplayer.com/terms" }, "node_modules/throat": { diff --git a/package.json b/package.json index bd0dab6fd..809d1e673 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-theoplayer", - "version": "11.1.0", + "version": "11.2.0", "description": "A THEOplayer video component for react-native.", "main": "lib/commonjs/index", "module": "lib/module/index", @@ -70,7 +70,7 @@ "react": "^19.2.3", "react-native": "^0.84.1", "react-native-builder-bob": "^0.39.1", - "theoplayer": "^11.0.0", + "theoplayer": "^11.4.0", "typedoc": "^0.25.13", "typedoc-plugin-external-resolver": "^1.0.3", "typedoc-plugin-mdn-links": "^3.3.4", @@ -80,7 +80,7 @@ "peerDependencies": { "react": "*", "react-native": "*", - "theoplayer": "^11" + "theoplayer": "^11.4.0" }, "peerDependenciesMeta": { "theoplayer": { diff --git a/react-native-theoplayer.podspec b/react-native-theoplayer.podspec index 75885c65d..4ae211870 100644 --- a/react-native-theoplayer.podspec +++ b/react-native-theoplayer.podspec @@ -43,37 +43,37 @@ Pod::Spec.new do |s| # THEOplayer Dependency puts "Adding THEOplayerSDK-core" - s.dependency "THEOplayerSDK-core", "~> 11.0" + s.dependency "THEOplayerSDK-core", "~> 11.4" # THEOlive Dependency puts "Adding THEOplayer-Integration-THEOlive" - s.dependency "THEOplayer-Integration-THEOlive", "~> 11.0" + s.dependency "THEOplayer-Integration-THEOlive", "~> 11.4" # Feature based integration dependencies if theofeatures.include?("GOOGLE_IMA") puts "Adding THEOplayer-Integration-GoogleIMA" - s.dependency "THEOplayer-Integration-GoogleIMA", "~> 11.0" + s.dependency "THEOplayer-Integration-GoogleIMA", "~> 11.4" end if theofeatures.include?("CHROMECAST") puts "Adding THEOplayer-Integration-GoogleCast" - s.ios.dependency "THEOplayer-Integration-GoogleCast", "~> 11.0" + s.ios.dependency "THEOplayer-Integration-GoogleCast", "~> 11.4" end if theofeatures.include?("THEO_ADS") puts "Adding THEOplayer-Integration-THEOads" - s.dependency "THEOplayer-Integration-THEOads", "~> 11.0" + s.dependency "THEOplayer-Integration-THEOads", "~> 11.4" end if theofeatures.include?("MILLICAST") puts "Adding THEOplayer-Integration-Millicast" - s.dependency "THEOplayer-Integration-Millicast", "~> 11.0" + s.dependency "THEOplayer-Integration-Millicast", "~> 11.4" end # Feature based connector dependencies if theofeatures.include?("SIDELOADED_TEXTTRACKS") puts "Adding THEOplayer-Connector-SideloadedSubtitle" - s.dependency "THEOplayer-Connector-SideloadedSubtitle", "~> 11.0" + s.dependency "THEOplayer-Connector-SideloadedSubtitle", "~> 11.0", "> 11.0.1" end end diff --git a/src/api/ads/Ad.ts b/src/api/ads/Ad.ts index ee71988b6..0ffaf0ea0 100644 --- a/src/api/ads/Ad.ts +++ b/src/api/ads/Ad.ts @@ -1,5 +1,6 @@ import type { AdBreak } from './AdBreak'; import type { CompanionAd } from 'theoplayer'; +import { AdIntegrationKind, CustomAdIntegrationKind } from '../source/ads/Ads'; /** * Represents a VAST creative. It is either a linear or non-linear ad. @@ -17,15 +18,12 @@ export interface Ad { adSystem: string | undefined; /** - * The integration of the ad, represented by a value from the following list: - *
- `'theo'` - *
- `'google-ima'` - *
- `'google-dai'` - *
- `'freewheel'` + * The integration of the ad, represented by a value from {@link AdIntegrationKind} + * or {@link CustomAdIntegrationKind | the identifier of a custom integration}. * - * @defaultValue `'theo'` + * @defaultValue `'csai'` */ - integration?: string; + integration?: AdIntegrationKind | CustomAdIntegrationKind; /** * The type of the ad, represented by a value from the following list: diff --git a/src/api/ads/AdBreak.ts b/src/api/ads/AdBreak.ts index fcd25b60e..aa0712a3e 100644 --- a/src/api/ads/AdBreak.ts +++ b/src/api/ads/AdBreak.ts @@ -5,6 +5,7 @@ * @public */ import type { Ad } from './Ad'; +import { AdIntegrationKind, CustomAdIntegrationKind } from '../source/ads/Ads'; /** * Represents an ad break in the VMAP specification or an ad pod in the VAST specification. @@ -14,13 +15,10 @@ import type { Ad } from './Ad'; */ export interface AdBreak { /** - * The integration of the ad break, represented by a value from the following list: - *
- `'theo'` - *
- `'google-ima'` - *
- `'google-dai'` - *
- `'freewheel'` + * The integration of the ad break, represented by a value from {@link AdIntegrationKind} + * or {@link CustomAdIntegrationKind | the identifier of a custom integration}. */ - integration: string | undefined; + integration: AdIntegrationKind | CustomAdIntegrationKind | undefined; /** * List of ads which will be played sequentially at the ad break's time offset. diff --git a/src/api/source/ads/Ads.ts b/src/api/source/ads/Ads.ts index b8df9c24b..b50d6d63d 100644 --- a/src/api/source/ads/Ads.ts +++ b/src/api/source/ads/Ads.ts @@ -44,10 +44,8 @@ export interface AdSource { export interface AdDescription { /** * The integration of the ad break. - * - * @defaultValue `'csai'` */ - integration?: AdIntegrationKind; + integration?: AdIntegrationKind | CustomAdIntegrationKind; /** * Whether the ad replaces playback of the content. @@ -116,3 +114,10 @@ export enum AdIntegrationKind { csai = 'csai', theoads = 'theoads', } + +/** + * The identifier of a custom ad integration. + * + * @category Ads + */ +export type CustomAdIntegrationKind = string & {}; diff --git a/src/api/source/ads/TheoAdDescription.ts b/src/api/source/ads/TheoAdDescription.ts index 6f3bcfc7f..066f18b07 100644 --- a/src/api/source/ads/TheoAdDescription.ts +++ b/src/api/source/ads/TheoAdDescription.ts @@ -107,6 +107,14 @@ export interface TheoAdDescription extends AdDescription { * The amount of seconds we wait to initialize THEOads. */ initializationDelay?: number; + + /** + * The URL of the break manifest. + * + * @remarks + *
- The break manifest describes the upcoming ad breaks in the stream. + */ + breakManifestUrl?: string; } /** diff --git a/src/internal/adapter/theolive/TheoLiveWebAdapter.ts b/src/internal/adapter/theolive/TheoLiveWebAdapter.ts index 6376211ae..3911d6956 100644 --- a/src/internal/adapter/theolive/TheoLiveWebAdapter.ts +++ b/src/internal/adapter/theolive/TheoLiveWebAdapter.ts @@ -10,17 +10,8 @@ export class TheoLiveWebAdapter implements TheoLiveAPI { } get latencies(): Promise { - const webLatencies = this._player.hesp?.latencies; - if (webLatencies) { - return Promise.resolve({ - engineLatency: webLatencies?.engine, - distributionLatency: webLatencies?.distribution, - playerLatency: webLatencies?.player, - theoliveLatency: webLatencies?.theolive, - }); - } else { - return Promise.reject('latencies not available'); - } + console.warn('The THEOlive latencies metrics are not available'); + return Promise.resolve({}); } set authToken(token: string) {