diff --git a/README.md b/README.md index 6509043..faf6bf2 100644 --- a/README.md +++ b/README.md @@ -59,5 +59,6 @@ the following connectors, readily available to be used by `react-native-theoplay | EZDRM | ✓ (*) | ✓ (*) | ✓ | | Verimatrix MultiDRM Core DRM | ✓ | ✓ | ✓ | | Anvato | ✓ | ✗ | ✓ | +| PallyCon | ✗ | ✗ | ✓ | (*) No connector is needed for these DRM vendors; it is handled by the player's default DRM flow. diff --git a/src/pallycon/PallyConConfiguration.ts b/src/pallycon/PallyConConfiguration.ts new file mode 100644 index 0000000..bc5f540 --- /dev/null +++ b/src/pallycon/PallyConConfiguration.ts @@ -0,0 +1,26 @@ +import type { DRMConfiguration } from 'react-native-theoplayer'; + +/** + * The identifier of the PallyCon integration. + */ +export type PallyConIntegrationID = 'pallycon'; + +/** + * Describes the configuration of the PallyCon DRM integration. + * + * ``` + * const drmConfiguration = { + * integration : 'pallycon', + * fairplay: { + * certificateURL: 'yourPallyConCertificateUrl', + * licenseAcquisitionURL: 'yourPallyConLicenseAcquisitionURL' + * } + * } + * ``` + */ +export interface PallyConDrmConfiguration extends DRMConfiguration { + /** + * The identifier of the DRM integration. + */ + integration: PallyConIntegrationID; +} diff --git a/src/pallycon/PallyConFairplayContentProtectionIntegration.ts b/src/pallycon/PallyConFairplayContentProtectionIntegration.ts new file mode 100644 index 0000000..75b5a32 --- /dev/null +++ b/src/pallycon/PallyConFairplayContentProtectionIntegration.ts @@ -0,0 +1,41 @@ +import type { ContentProtectionIntegration, LicenseRequest, LicenseResponse, MaybeAsync } from 'react-native-theoplayer'; +import type { PallyConDrmConfiguration } from './PallyConConfiguration'; +import { fromBase64StringToUint8Array, fromStringToUint8Array, fromUint8ArrayToBase64String, fromUint8ArrayToString } from "react-native-theoplayer"; + +export class PallyConFairplayContentProtectionIntegration implements ContentProtectionIntegration { + static readonly DEFAULT_LICENSE_URL = 'https://license-global.pallycon.com/ri/licenseManager.do'; + + private readonly contentProtectionConfiguration: PallyConDrmConfiguration; + private contentId: string | undefined = undefined; + + constructor(configuration: PallyConDrmConfiguration) { + this.contentProtectionConfiguration = configuration; + } + + onLicenseRequest(request: LicenseRequest): MaybeAsync | BufferSource> { + if (!this.contentProtectionConfiguration.fairplay?.licenseAcquisitionURL) { + throw new Error('The FairPlay PallyCon license url has not been correctly configured.'); + } + request.url = this.contentProtectionConfiguration.fairplay?.licenseAcquisitionURL; + request.headers = { + ...request.headers, + ...this.contentProtectionConfiguration.fairplay?.headers + }; + if (!this.contentId) { + throw new Error('The FairPlay PallyCon content ID has not been correctly configured.'); + } + const licenseParameters = `spc=${encodeURIComponent(fromUint8ArrayToBase64String(request.body!))}&assetId=${encodeURIComponent(this.contentId)}`; + request.body = fromStringToUint8Array(licenseParameters); + return request; + } + + onLicenseResponse?(response: LicenseResponse): MaybeAsync { + return fromBase64StringToUint8Array(fromUint8ArrayToString(response.body)); + } + + extractFairplayContentId(skdUrl: string): string { + // drop the 'skd://' part + this.contentId = skdUrl.substring(6, skdUrl.length); + return this.contentId; + } +} diff --git a/src/pallycon/PallyConFairplayContentProtectionIntegrationFactory.ts b/src/pallycon/PallyConFairplayContentProtectionIntegrationFactory.ts new file mode 100644 index 0000000..6407c09 --- /dev/null +++ b/src/pallycon/PallyConFairplayContentProtectionIntegrationFactory.ts @@ -0,0 +1,9 @@ +import type { ContentProtectionIntegration, ContentProtectionIntegrationFactory } from 'react-native-theoplayer'; +import type { PallyConDrmConfiguration } from './PallyConConfiguration'; +import { PallyConFairplayContentProtectionIntegration } from './PallyConFairplayContentProtectionIntegration'; + +export class PallyConFairplayContentProtectionIntegrationFactory implements ContentProtectionIntegrationFactory { + build(configuration: PallyConDrmConfiguration): ContentProtectionIntegration { + return new PallyConFairplayContentProtectionIntegration(configuration); + } +}