diff --git a/.eslintignore b/.eslintignore index bc5a7dd2..996a513b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,8 +1,11 @@ +/.expo lib/ node_modules/ example/node_modules/ +example/android/ example/ios/ example/ios/Pods/ integration_test/node_modules/ +integration_test/android/ integration_test/ios/ integration_test/ios/Pods/ diff --git a/.eslintrc.js b/.eslintrc.js index 5261033c..56bfd59b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,8 +1,7 @@ -const prettierConfig = require('./prettier.config'); module.exports = { - root: true, - extends: ['@react-native-community', 'prettier'], + extends: ['expo', 'prettier'], + plugins: ['prettier'], rules: { - 'prettier/prettier': ['error', prettierConfig], + 'prettier/prettier': 'error', }, }; diff --git a/.github/actions/setup-environment/action.yml b/.github/actions/setup-environment/action.yml index 619aa3c3..ae599d69 100644 --- a/.github/actions/setup-environment/action.yml +++ b/.github/actions/setup-environment/action.yml @@ -22,26 +22,30 @@ inputs: description: Setup Java and Gradle cache default: false required: false + tv: + description: Prebuild TV projects + default: false + required: false runs: using: composite steps: - name: Set up Java if: ${{ inputs.java == 'true' }} - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: - distribution: 'zulu' + distribution: 'temurin' java-version: '17' - name: Set up Gradle cache if: ${{ inputs.java == 'true' }} - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v4 with: cache-read-only: ${{ github.ref != 'refs/heads/development' }} - name: Setup node and npm registry if: ${{ inputs.node == 'true' || inputs.subprojects == 'true' }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '20' registry-url: 'https://registry.npmjs.org/' @@ -55,17 +59,46 @@ runs: - name: Install node_modules (example/) if: ${{ inputs.subprojects == 'true' }} shell: bash - run: yarn install --frozen-lockfile --cwd example + run: | + yarn install --cwd example + cp example/.env.example example/.env + + - name: Generate native projects for mobile platforms + if: ${{ inputs.subprojects == 'true' && inputs.tv == 'false' }} + shell: bash + run: | + .github/scripts/smart-prebuild.sh example + env: + NSUnbufferedIO: YES + + - name: Generate native projects for TV platforms + if: ${{ inputs.subprojects == 'true' && inputs.tv == 'true' }} + shell: bash + run: | + .github/scripts/smart-prebuild.sh example tv + env: + NSUnbufferedIO: YES - name: Install node_modules (integration_test/) if: ${{ inputs.subprojects == 'true' }} shell: bash - run: yarn install --frozen-lockfile --cwd integration_test + run: | + yarn install --cwd integration_test + cp integration_test/.env.example integration_test/.env + .github/scripts/smart-prebuild.sh integration-test + env: + NSUnbufferedIO: YES + + - name: Setup Gradle CI properties + if: ${{ inputs.java == 'true' && inputs.subprojects == 'true' }} + shell: bash + run: | + .github/scripts/setup-gradle-properties.sh - uses: maxim-lobanov/setup-xcode@v1 if: ${{ inputs.ios == 'true' }} with: - xcode-version: '15.4' + xcode-version: '16.4' - name: Install dependencies if: ${{ inputs.ios == 'true' || inputs.brew == 'true' }} diff --git a/.github/gradle-ci.properties b/.github/gradle-ci.properties new file mode 100644 index 00000000..446009e1 --- /dev/null +++ b/.github/gradle-ci.properties @@ -0,0 +1,11 @@ +org.gradle.caching=true +org.gradle.parallel=true +org.gradle.daemon=false +org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.workers.max=4 +org.gradle.configuration-cache=true +kotlin.incremental=true +org.gradle.welcome=never +org.gradle.dependency.verification=off +org.gradle.console=plain +reactNativeArchitectures=x86,x86_64 diff --git a/.github/scripts/setup-gradle-properties.sh b/.github/scripts/setup-gradle-properties.sh new file mode 100755 index 00000000..db5e4bf1 --- /dev/null +++ b/.github/scripts/setup-gradle-properties.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +CI_GRADLE_PROPS="${SCRIPT_DIR}/../gradle-ci.properties" + +setup_gradle_properties() { + local android_dir="$1" + + if [ -d "$android_dir" ]; then + if [ -f "$android_dir/gradle.properties" ]; then + cp "$android_dir/gradle.properties" "$android_dir/gradle.properties.backup" + fi + + echo "" >> "$android_dir/gradle.properties" + cat "$CI_GRADLE_PROPS" >> "$android_dir/gradle.properties" + fi +} + +setup_gradle_properties "example/android" +setup_gradle_properties "integration_test/android" diff --git a/.github/scripts/smart-prebuild.sh b/.github/scripts/smart-prebuild.sh new file mode 100755 index 00000000..22568a6b --- /dev/null +++ b/.github/scripts/smart-prebuild.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +YARN_PROJECT="$1" +PLATFORM_TYPE="$2" + +if [ -z "$YARN_PROJECT" ]; then + echo "Usage: smart-prebuild.sh [platform]" + exit 1 +fi + +PROJECT_FOLDER="${YARN_PROJECT//-/_}" +ANDROID_DIR="${PROJECT_FOLDER}/android" +IOS_DIR="${PROJECT_FOLDER}/ios" + +if [ -d "$ANDROID_DIR" ] && [ -f "$ANDROID_DIR/app/build.gradle" ] && + [ -d "$IOS_DIR" ] && [ -f "$IOS_DIR/Podfile" ]; then + exit 0 +fi + +if [ -d "$ANDROID_DIR" ] && [ ! -f "$ANDROID_DIR/app/build.gradle" ]; then + rm -rf "$ANDROID_DIR" +fi + +if [ -d "$IOS_DIR" ] && [ ! -f "$IOS_DIR/Podfile" ]; then + rm -rf "$IOS_DIR" +fi + +COMMAND="prebuild" +if [ -n "$PLATFORM_TYPE" ]; then + COMMAND="${COMMAND}:${PLATFORM_TYPE}" +fi + +yarn "$YARN_PROJECT" "$COMMAND" --clean diff --git a/.github/scripts/update_player_sdk_update_changelog.py b/.github/scripts/update_player_sdk_update_changelog.py new file mode 100755 index 00000000..e6ae0ffb --- /dev/null +++ b/.github/scripts/update_player_sdk_update_changelog.py @@ -0,0 +1,176 @@ +"""Update the Unreleased → Changed section in CHANGELOG.md with a new Player SDK version entry. + +Usage: + python3 update_player_sdk_update_changelog.py + +Notes: +- More robust handling of line endings (LF/CRLF), whitespace, and semver (incl. pre-release/build). +- Idempotently replaces existing SDK update line for the given platform or inserts at the top of the Changed list. +""" +from __future__ import annotations + +import sys +import re +from typing import Tuple + + +CHANGELOG_FILE = "CHANGELOG.md" + +# Common headers and labels +HEADER_CHANGELOG = "# Changelog" +HEADER_UNRELEASED = "## [Unreleased]" +SUBHEADER_CHANGED = "### Changed" + +# CLI / messages +MESSAGE_USAGE = "Usage: python3 update_player_sdk_update_changelog.py.py " +ERROR_INVALID_PLATFORM = "Error: Invalid platform. Must be 'android' or 'ios'." +ERROR_INVALID_VERSION = ( + "Error: Invalid version. Must be SemVer, e.g. 1.2.3, 1.2.3-beta.1, or 1.2.3+build." +) +MESSAGE_SUCCESS = "Changelog updated successfully." +MESSAGE_ADDING_ENTRY = ( + "Adding entry for platform '{platform}' with version '{version}' to {file}" +) + +# Platforms and labels +PLATFORM_ANDROID = "android" +PLATFORM_IOS = "ios" +PLATFORMS = {PLATFORM_ANDROID: "Android", PLATFORM_IOS: "iOS"} + +# SemVer: MAJOR.MINOR.PATCH with optional -pre-release and +build metadata +SEMVER_RE = r"\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?" + +# Entry line template pieces +ENTRY_LINE_PREFIX = "- Update Bitmovin's native {platform} SDK version to `" + + +def normalize_newlines(text: str) -> str: + """Normalize CRLF to LF to make regex handling deterministic.""" + return text.replace("\r\n", "\n") + + +def load_changelog(path: str) -> str: + try: + with open(path, "r", encoding="utf-8") as f: + return normalize_newlines(f.read()) + except FileNotFoundError: + print(f"Error: {path} not found.") + sys.exit(1) + + +def write_changelog(path: str, content: str) -> None: + # Collapse 3+ blank lines to 2 to avoid excessive spacing + content = re.sub(r"\n{3,}", "\n\n", content) + with open(path, "w", encoding="utf-8", newline="\n") as f: + f.write(content) + + +def build_entry(platform_key: str, version: str) -> Tuple[str, re.Pattern[str]]: + platform_label = PLATFORMS[platform_key] + entry_prefix = ENTRY_LINE_PREFIX.format(platform=platform_label) + new_entry = f"{entry_prefix}{version}`" + # Pattern to find an existing entry for this platform regardless of version + existing_pattern = re.compile( + rf"^{re.escape(entry_prefix)}{SEMVER_RE}`$", + flags=re.MULTILINE, + ) + return new_entry, existing_pattern + + +def update_unreleased_changed_section(content: str, platform_key: str, version: str) -> str: + # Find Unreleased section (tolerate optional text after header and CRLF) + unreleased_section_pattern = re.compile( + rf"({re.escape(HEADER_UNRELEASED)}[^\n]*\n)(.*?)(?=\n## \[|\Z)", + flags=re.DOTALL, + ) + + match = unreleased_section_pattern.search(content) + + new_entry, existing_pattern = build_entry(platform_key, version) + + if match: + unreleased_header = match.group(1) + unreleased_body = match.group(2) + + # Locate or create the '### Changed' subsection inside Unreleased + changed_subsection_pattern = re.compile( + rf"({re.escape(SUBHEADER_CHANGED)}[^\n]*\n\n)(.*?)(?=\n## \[|\n### |\Z)", + flags=re.DOTALL, + ) + changed_match = changed_subsection_pattern.search(unreleased_body) + + if changed_match: + changed_header = changed_match.group(1) + changed_body = changed_match.group(2) + + if existing_pattern.search(changed_body): + # Replace existing line for this platform + new_changed_body = existing_pattern.sub(new_entry, changed_body) + else: + # Prepend new entry to keep fresh updates at the top + new_changed_body = new_entry + "\n" + changed_body + + new_unreleased_body = changed_subsection_pattern.sub( + changed_header + new_changed_body, + unreleased_body, + ) + else: + # Create the Changed subsection with our new entry + suffix = "\n" if not unreleased_body.endswith("\n") else "" + new_unreleased_body = ( + unreleased_body + + suffix + + f"\n{SUBHEADER_CHANGED}\n\n" + + new_entry + + "\n" + ) + + # Reassemble content with the updated Unreleased section + return unreleased_section_pattern.sub( + unreleased_header + new_unreleased_body, content, count=1 + ) + + # No Unreleased section – insert one after '# Changelog' header if present + changelog_header_pattern = re.compile(rf"({re.escape(HEADER_CHANGELOG)}[^\n]*\n)") + header_match = changelog_header_pattern.search(content) + + new_section = f"\n{HEADER_UNRELEASED}\n\n{SUBHEADER_CHANGED}\n\n" + new_entry + "\n" + + if header_match: + insert_pos = header_match.end() + return content[:insert_pos] + new_section + content[insert_pos:] + + # If even the top-level header is missing, prepend a standard header + return f"{HEADER_CHANGELOG}\n" + new_section + content + + +def validate_inputs(version: str, platform: str) -> None: + if platform not in PLATFORMS: + print(ERROR_INVALID_PLATFORM) + sys.exit(1) + if not re.fullmatch(SEMVER_RE, version): + print(ERROR_INVALID_VERSION) + sys.exit(1) + + +def main() -> None: + if len(sys.argv) != 3: + print(MESSAGE_USAGE) + sys.exit(1) + + version = sys.argv[1].strip() + platform = sys.argv[2].strip().lower() + + validate_inputs(version, platform) + + print(MESSAGE_ADDING_ENTRY.format(platform=platform, version=version, file=CHANGELOG_FILE)) + + content = load_changelog(CHANGELOG_FILE) + new_content = update_unreleased_changed_section(content, platform, version) + + write_changelog(CHANGELOG_FILE, new_content) + print(MESSAGE_SUCCESS) + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/alpha-release.yml b/.github/workflows/alpha-release.yml new file mode 100644 index 00000000..dfe4a575 --- /dev/null +++ b/.github/workflows/alpha-release.yml @@ -0,0 +1,85 @@ +name: Manual Alpha Release + +run-name: Alpha release for v${{ github.event.inputs.version }} + +on: + workflow_dispatch: + inputs: + version: + description: 'The base semantic version for the alpha release (e.g., 1.0.0)' + required: true + +env: + LC_ALL: en_US.UTF-8 + LANG: en_US.UTF-8 + +concurrency: + group: alpha-release-${{ github.event.inputs.version }} + cancel-in-progress: true + +jobs: + publish-alpha: + name: Publish Alpha Release + runs-on: ubuntu-latest + permissions: + contents: write # Needed to create commits and tags + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + # Fetch all history for all tags and branches for version calculation + fetch-depth: 0 + ssh-key: ${{ secrets.RELEASE_DEPLOY_KEY }} + + - name: Setup Git User + run: | + git config --global user.name "Bitmovin Release Automation" + git config --global user.email "support@bitmovin.com" + + - name: Setup Environment + uses: ./.github/actions/setup-environment + with: + node: true + + - name: Determine Alpha Version + id: versioner + run: | + BASE_VERSION="${{ github.event.inputs.version }}" + # Find the latest alpha tag for this base version to increment it + LATEST_ALPHA_TAG=$(git tag --list "v${BASE_VERSION}-alpha.*" | sort -V | tail -n 1) + if [[ -z "$LATEST_ALPHA_TAG" ]]; then + # No alpha tag exists yet, start with .0 + NEXT_VERSION="${BASE_VERSION}-alpha.0" + else + # Increment the existing alpha tag + LATEST_ALPHA_VERSION=$(echo $LATEST_ALPHA_TAG | sed 's/v//') + # Use awk to increment the last part of the version + NEXT_VERSION=$(echo $LATEST_ALPHA_VERSION | awk -F. -v OFS=. '{$NF++;print}') + fi + echo "FINAL_VERSION=${NEXT_VERSION}" >> $GITHUB_ENV + + - name: Update Package Version + run: | + npm version ${{ env.FINAL_VERSION }} --no-git-tag-version --allow-same-version + + - name: Build TypeScript files + run: yarn build + + - name: Commit version bump + run: | + git add . + git commit --no-verify -m "chore(release): alpha release ${{ env.FINAL_VERSION }}" + + - name: Create Git Tag + run: | + git tag v${{ env.FINAL_VERSION }} + + - name: Publish to NPM with alpha tag + run: npm publish --tag alpha + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Push changes to repository + run: | + git push + git push origin v${{ env.FINAL_VERSION }} diff --git a/.github/workflows/ci-android.yml b/.github/workflows/ci-android.yml index e1b692c8..2a4ef776 100644 --- a/.github/workflows/ci-android.yml +++ b/.github/workflows/ci-android.yml @@ -8,19 +8,17 @@ on: - 'package.json' - 'yarn.lock' - 'android/**' - - 'example/android/**' - 'example/package.json' - 'example/yarn.lock' push: - branches: [development] + branches: [development, development-v0] paths: - '.github/workflows/ci-android.yml' - '.github/actions/**' - 'package.json' - 'yarn.lock' - 'android/**' - - 'example/android/**' - 'example/package.json' - 'example/yarn.lock' @@ -29,7 +27,7 @@ concurrency: cancel-in-progress: true env: - NO_FLIPPER: 1 + GRADLE_USER_HOME: ${{ github.workspace }}/.gradle-ci jobs: code-style-android: @@ -45,11 +43,10 @@ jobs: java: true - name: Check code style - run: ./gradlew ktlintCheck - working-directory: android + run: yarn lint:android test-build-android: - name: Build Android + name: Build Android Example App runs-on: ubuntu-latest steps: - name: Checkout @@ -62,10 +59,180 @@ jobs: node: true subprojects: true + - name: Setup Gradle Build Cache + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + ${{ github.workspace }}/.gradle-ci + example/android/.gradle + example/android/app/build + example/android/build + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'example/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Cache Android Prebuild + id: android-prebuild-cache + uses: actions/cache@v4 + with: + path: | + example/android + key: ${{ runner.os }}-android-prebuild-${{ hashFiles('example/yarn.lock', 'example/app.json', 'plugin/**', 'android/**') }} + restore-keys: | + ${{ runner.os }}-android-prebuild- + - name: Build Android example - run: ./gradlew assembleDebug --build-cache - working-directory: example/android + id: build + run: | + START_TIME=$(date +%s) + cd example/android + ./gradlew assembleDebug --quiet --console=plain --warning-mode=none \ + --build-cache --parallel --max-workers=4 --no-daemon \ + --no-configuration-cache \ + -PreactNativeArchitectures=x86,x86_64 + END_TIME=$(date +%s) + echo "duration=$((END_TIME - START_TIME))" >> $GITHUB_OUTPUT + + - name: Report build time + run: echo "✅ Build completed in ${{ steps.build.outputs.duration }} seconds" + + - name: Upload build cache artifacts + if: success() + uses: actions/upload-artifact@v4 + with: + name: android-example-build-cache + path: | + ~/.gradle/caches/build-cache-* + example/android/app/build/intermediates + retention-days: 1 + + test-build-android-integration-tests: + name: Build Android Integration Tests + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Environment + uses: ./.github/actions/setup-environment + with: + java: true + node: true + subprojects: true + + - name: Setup Gradle Build Cache + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + ${{ github.workspace }}/.gradle-ci + integration_test/android/.gradle + integration_test/android/app/build + integration_test/android/build + key: ${{ runner.os }}-gradle-integration-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'integration_test/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-gradle-integration- + ${{ runner.os }}-gradle- + + - name: Cache Android Integration Prebuild + id: android-integration-prebuild-cache + uses: actions/cache@v4 + with: + path: | + integration_test/android + key: ${{ runner.os }}-android-integration-prebuild-${{ hashFiles('integration_test/yarn.lock', 'integration_test/app.json', 'plugin/**', 'android/**') }} + restore-keys: | + ${{ runner.os }}-android-integration-prebuild- - name: Build Android integration test host app - run: ./gradlew assembleDebug --build-cache - working-directory: integration_test/android + id: build + run: | + START_TIME=$(date +%s) + cd integration_test/android + ./gradlew assembleDebug --quiet --console=plain --warning-mode=none \ + --build-cache --parallel --max-workers=4 --no-daemon \ + --no-configuration-cache \ + -PreactNativeArchitectures=x86,x86_64 + END_TIME=$(date +%s) + echo "duration=$((END_TIME - START_TIME))" >> $GITHUB_OUTPUT + + - name: Report build time + run: echo "✅ Build completed in ${{ steps.build.outputs.duration }} seconds" + + - name: Upload build cache artifacts + if: success() + uses: actions/upload-artifact@v4 + with: + name: android-integration-build-cache + path: | + ~/.gradle/caches/build-cache-* + integration_test/android/app/build/intermediates + retention-days: 1 + + test-build-android-tv: + name: Build Android TV Example App + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Environment + uses: ./.github/actions/setup-environment + with: + java: true + node: true + subprojects: true + tv: true + + - name: Setup Gradle Build Cache + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + ${{ github.workspace }}/.gradle-ci + example/android/.gradle + example/android/app/build + example/android/build + key: ${{ runner.os }}-gradle-tv-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', 'example/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-gradle-tv- + ${{ runner.os }}-gradle- + + - name: Cache Android TV Prebuild + id: android-tv-prebuild-cache + uses: actions/cache@v4 + with: + path: | + example/android + key: ${{ runner.os }}-android-tv-prebuild-${{ hashFiles('example/yarn.lock', 'example/app.json', 'plugin/**', 'android/**') }} + restore-keys: | + ${{ runner.os }}-android-tv-prebuild- + + - name: Build Android TV example + id: build + run: | + START_TIME=$(date +%s) + cd example/android + ./gradlew assembleDebug --quiet --console=plain --warning-mode=none \ + --build-cache --parallel --max-workers=4 --no-daemon \ + --no-configuration-cache \ + -PreactNativeArchitectures=x86,x86_64 + END_TIME=$(date +%s) + echo "duration=$((END_TIME - START_TIME))" >> $GITHUB_OUTPUT + + - name: Report build time + run: echo "✅ Build completed in ${{ steps.build.outputs.duration }} seconds" + + - name: Upload build cache artifacts + if: success() + uses: actions/upload-artifact@v4 + with: + name: android-tv-build-cache + path: | + ~/.gradle/caches/build-cache-* + example/android/app/build/intermediates + retention-days: 1 diff --git a/.github/workflows/ci-ios-tvos.yml b/.github/workflows/ci-ios-tvos.yml index 8016f762..8991a2b6 100644 --- a/.github/workflows/ci-ios-tvos.yml +++ b/.github/workflows/ci-ios-tvos.yml @@ -1,61 +1,83 @@ name: CI (iOS & tvOS) on: - pull_request: + push: + branches: + - development paths: - - '.github/workflows/ci-ios-tvos.yml' - - '.github/actions/**' - - 'package.json' - - 'yarn.lock' - 'ios/**' - - 'RNBitmovinPlayer.podspec' - - '.swiftlint.yml' - - 'Brewfile.lock.json' + - 'plugin/**' - 'example/ios/**' - - 'example/package.json' - - 'example/yarn.lock' - - push: - branches: [development] - paths: - - '.github/workflows/ci-ios-tvos.yml' - - '.github/actions/**' + - 'integration_test/ios/**' - 'package.json' - 'yarn.lock' + - '**/Podfile*' + - '**/*.podspec' + - '**/*.xcodeproj/**' + - '**/*.xcworkspace/**' + - '**/Info.plist' + - '.github/workflows/ci-ios-tvos.yml' + - '.github/scripts/**' + - '.github/actions/**' + - '**/app.config.*' + - '**/expo.json' + - '**/build-ios.sh' + - '**/build-tvos.sh' + - 'example/scripts/**' + - 'integration_test/scripts/**' + + pull_request: + types: [opened, synchronize, reopened] + paths: - 'ios/**' - - 'RNBitmovinPlayer.podspec' - - '.swiftlint.yml' - - 'Brewfile.lock.json' + - 'plugin/**' - 'example/ios/**' - - 'example/package.json' - - 'example/yarn.lock' + - 'integration_test/ios/**' + - 'package.json' + - 'yarn.lock' + - '**/Podfile*' + - '**/*.podspec' + - '**/*.xcodeproj/**' + - '**/*.xcworkspace/**' + - '**/Info.plist' + - '.github/workflows/ci-ios-tvos.yml' + - '.github/scripts/**' + - '.github/actions/**' + - '**/app.config.*' + - '**/expo.json' + - '**/build-ios.sh' + - '**/build-tvos.sh' + - 'example/scripts/**' + - 'integration_test/scripts/**' concurrency: group: ci-ios-tvos-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true env: - NO_FLIPPER: 1 + NSUnbufferedIO: YES + IOS_DESTINATION: 'generic/platform=iOS Simulator' + TVOS_DESTINATION: 'generic/platform=tvOS Simulator' + XCODEBUILD_COMMON_FLAGS: '-parallelizeTargets -jobs 4 ONLY_ACTIVE_ARCH=YES ARCHS=x86_64 VALID_ARCHS=x86_64 DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO SWIFT_COMPILATION_MODE=wholemodule ENABLE_TESTABILITY=NO GCC_OPTIMIZATION_LEVEL=0 SWIFT_OPTIMIZATION_LEVEL=-Onone ENABLE_BITCODE=NO' jobs: code-style-ios: name: Code style iOS - runs-on: macOS-14 + runs-on: ubuntu-latest + timeout-minutes: 5 steps: - name: Checkout uses: actions/checkout@v4 - - name: Setup Environment - uses: ./.github/actions/setup-environment + - name: SwiftLint + uses: norio-nomura/action-swiftlint@3.2.1 with: - ios: true + args: ios --strict --quiet - - name: Check code style - run: swiftlint --strict - - test-build-ios: - name: Build iOS - runs-on: macOS-14 + ios-example-build: + name: Build iOS Example App + runs-on: macOS-15 + timeout-minutes: 25 steps: - name: Checkout uses: actions/checkout@v4 @@ -67,46 +89,44 @@ jobs: subprojects: true ios: true - - name: Restore Pods cache - id: pods-cache-restore - uses: actions/cache/restore@v3 + - name: Cache iOS Example Prebuild + id: ios-example-prebuild-cache + uses: actions/cache@v4 with: path: | - .cocoapods-cache - example/ios/Pods - key: pods-${{ hashFiles('example/ios/Podfile.lock') }} - restore-keys: pods- + example/ios + key: ${{ runner.os }}-ios-example-prebuild-${{ hashFiles('example/yarn.lock', 'example/app.json', 'plugin/**', 'ios/**') }} + restore-keys: | + ${{ runner.os }}-ios-example-prebuild- - - name: Install pods - run: yarn pods - env: - CP_HOME_DIR: ${{ github.workspace }}/.cocoapods-cache - - - name: Save Pods cache - if: steps.pods-cache-restore.outputs.cache-hit != 'true' - uses: actions/cache/save@v3 + - name: Cache CocoaPods Dependencies + uses: actions/cache@v4 with: path: | - .cocoapods-cache + ~/Library/Caches/CocoaPods example/ios/Pods - integration_test/ios/Pods - key: ${{ steps.pods-cache-restore.outputs.cache-primary-key }} + key: ${{ runner.os }}-ios-example-pods-${{ hashFiles('ios/RNBitmovinPlayer.podspec', 'example/ios/Podfile.lock') }} + restore-keys: | + ${{ runner.os }}-ios-example-pods- - - name: Build iOS example - run: set -o pipefail && xcodebuild -workspace BitmovinPlayerReactNativeExample.xcworkspace -scheme BitmovinPlayerReactNativeExample -configuration Debug build CODE_SIGNING_ALLOWED='NO' | xcpretty - working-directory: example/ios - env: - NSUnbufferedIO: YES + - name: Install CocoaPods Dependencies + run: yarn example pods - - name: Build iOS integration test host app - run: set -o pipefail && xcodebuild -workspace IntegrationTest.xcworkspace -scheme IntegrationTest -configuration Debug build CODE_SIGNING_ALLOWED='NO' | xcpretty - working-directory: integration_test/ios + - name: Build iOS Example App + id: build + run: | + START_TIME=$(date +%s) + yarn example build:ios --renderer github-actions --quiet + END_TIME=$(date +%s) + echo "duration=$((END_TIME - START_TIME))" >> $GITHUB_OUTPUT + echo "✅ Build completed in $((END_TIME - START_TIME)) seconds" env: - NSUnbufferedIO: YES + XCODEBUILD_ARGS: "-destination '${{ env.IOS_DESTINATION }}' ${{ env.XCODEBUILD_COMMON_FLAGS }}" - test-build-tvos: - name: Build tvOS - runs-on: macOS-14 + ios-integration-build: + name: Build iOS Integration Tests + runs-on: macOS-15 + timeout-minutes: 25 steps: - name: Checkout uses: actions/checkout@v4 @@ -118,33 +138,83 @@ jobs: subprojects: true ios: true - - name: Restore Pods cache - id: pods-cache-restore - uses: actions/cache/restore@v3 + - name: Cache iOS Integration Prebuild + id: ios-integration-prebuild-cache + uses: actions/cache@v4 with: path: | - .cocoapods-cache - example/ios/Pods - key: pods-${{ hashFiles('example/ios/Podfile.lock') }}-${{ hashFiles('integration_test/ios/Podfile.lock') }} - restore-keys: pods- + integration_test/ios + key: ${{ runner.os }}-ios-integration-prebuild-${{ hashFiles('integration_test/yarn.lock', 'integration_test/app.json', 'plugin/**', 'ios/**') }} + restore-keys: | + ${{ runner.os }}-ios-integration-prebuild- - - name: Install pods - run: yarn example pods + - name: Cache CocoaPods Dependencies + uses: actions/cache@v4 + with: + path: | + ~/Library/Caches/CocoaPods + integration_test/ios/Pods + key: ${{ runner.os }}-ios-integration-pods-${{ hashFiles('ios/RNBitmovinPlayer.podspec', 'integration_test/ios/Podfile.lock') }} + restore-keys: | + ${{ runner.os }}-ios-integration-pods- + + - name: Install CocoaPods Dependencies + run: yarn integration-test pods + + - name: Build iOS Integration Tests + id: build + run: | + START_TIME=$(date +%s) + yarn integration-test build:ios --renderer github-actions --quiet + END_TIME=$(date +%s) + echo "duration=$((END_TIME - START_TIME))" >> $GITHUB_OUTPUT + echo "✅ Build completed in $((END_TIME - START_TIME)) seconds" env: - CP_HOME_DIR: ${{ github.workspace }}/.cocoapods-cache + XCODEBUILD_ARGS: "-destination '${{ env.IOS_DESTINATION }}' ${{ env.XCODEBUILD_COMMON_FLAGS }}" + + tvos-example-build: + name: Build tvOS Example App + runs-on: macOS-15 + timeout-minutes: 30 + steps: + - name: Checkout + uses: actions/checkout@v4 - - name: Save Pods cache - if: steps.pods-cache-restore.outputs.cache-hit != 'true' - uses: actions/cache/save@v3 + - name: Setup Environment + uses: ./.github/actions/setup-environment + with: + node: true + subprojects: true + ios: true + tv: true + + - name: Cache tvOS Prebuild + id: tvos-prebuild-cache + uses: actions/cache@v4 with: path: | - .cocoapods-cache - example/ios/Pods - integration_test/ios/Pods - key: ${{ steps.pods-cache-restore.outputs.cache-primary-key }} + example/ios + key: ${{ runner.os }}-tvos-prebuild-${{ hashFiles('example/yarn.lock', 'example/app.json', 'plugin/**', 'ios/**') }} + restore-keys: | + ${{ runner.os }}-tvos-prebuild- - - name: Build tvOS example - run: set -o pipefail && xcodebuild -workspace BitmovinPlayerReactNativeExample.xcworkspace -scheme BitmovinPlayerReactNativeExample-tvOS -configuration Debug build CODE_SIGNING_ALLOWED='NO' | xcpretty - working-directory: example/ios + - name: Cache CocoaPods Dependencies + uses: actions/cache@v4 + with: + path: | + ~/Library/Caches/CocoaPods + example/ios/Pods + key: ${{ runner.os }}-tvos-pods-${{ hashFiles('ios/RNBitmovinPlayer.podspec', 'example/ios/Podfile.lock') }} + restore-keys: | + ${{ runner.os }}-tvos-pods- + + - name: Build tvOS Example App + id: build + run: | + START_TIME=$(date +%s) + yarn example build:tvos --renderer github-actions --quiet + END_TIME=$(date +%s) + echo "duration=$((END_TIME - START_TIME))" >> $GITHUB_OUTPUT + echo "✅ Build completed in $((END_TIME - START_TIME)) seconds" env: - NSUnbufferedIO: YES + XCODEBUILD_ARGS: "-destination '${{ env.TVOS_DESTINATION }}' ${{ env.XCODEBUILD_COMMON_FLAGS }}" diff --git a/.github/workflows/ci-typescript.yml b/.github/workflows/ci-typescript.yml index 90cb5757..cc004db3 100644 --- a/.github/workflows/ci-typescript.yml +++ b/.github/workflows/ci-typescript.yml @@ -16,13 +16,12 @@ on: - 'tsconfig.json' - 'typedoc.json' - '**yarn.lock' - - '**react-native.config.js' - '**metro.config.js' - '!*/android/**' - '!*/ios/**' push: - branches: [development] + branches: [development, development-v0] paths: - '.github/workflows/ci-typescript.yml' - '.github/actions/**' @@ -37,7 +36,6 @@ on: - 'tsconfig.json' - 'typedoc.json' - '**yarn.lock' - - '**react-native.config.js' - '**metro.config.js' - '!*/android/**' - '!*/ios/**' @@ -64,6 +62,12 @@ jobs: - name: Install node_modules run: yarn install --frozen-lockfile + - name: Install node_modules (example/) + run: yarn install --frozen-lockfile --cwd example + + - name: Install node_modules (integration_test/) + run: yarn install --frozen-lockfile --cwd integration_test + - name: Lint Typescript run: yarn lint @@ -90,8 +94,8 @@ jobs: - name: Install node_modules (integration_test/) run: yarn install --frozen-lockfile --cwd integration_test - - name: Compile TypeScript - run: yarn typescript + - name: Typecheck TypeScript + run: yarn typecheck:all test-build-docs: name: Build API docs diff --git a/.github/workflows/create-sdk-update-pr.yml b/.github/workflows/create-sdk-update-pr.yml index 870a43d5..39fd5d06 100644 --- a/.github/workflows/create-sdk-update-pr.yml +++ b/.github/workflows/create-sdk-update-pr.yml @@ -17,13 +17,10 @@ on: required: true type: string -env: - NO_FLIPPER: 1 - jobs: update: - name: Update SDK version - runs-on: macos-14 + name: Update SDK version on v1 + runs-on: macos-15 env: GH_TOKEN: ${{ secrets.GH_TOKEN }} steps: @@ -46,6 +43,60 @@ jobs: git push origin --delete ${{ steps.branching.outputs.branch_name }} || true git checkout -b ${{ steps.branching.outputs.branch_name }} + - name: Bump iOS player SDK version + if: ${{ inputs.sdk_name == 'ios' }} + run: | + sed -i '' 's/s\.dependency "BitmovinPlayer", ".*/s.dependency "BitmovinPlayer", "${{ inputs.version_number }}"/g' ios/RNBitmovinPlayer.podspec + + - name: Bump Android player SDK version + if: ${{ inputs.sdk_name == 'android' }} + run: | + sed -i '' "s/com.bitmovin.player:player:.*/com.bitmovin.player:player:${{ inputs.version_number }}'/g" android/build.gradle + sed -i '' "s/com.bitmovin.player:player-media-session:.*/com.bitmovin.player:player-media-session:${{ inputs.version_number }}'/g" android/build.gradle + + - name: Update Changelog entry + run: python3 .github/scripts/update_player_sdk_update_changelog.py ${{ inputs.version_number }} ${{ inputs.sdk_name }} + + - name: Commit version bump + run: | + git add ios/RNBitmovinPlayer.podspec android/build.gradle CHANGELOG.md + git commit --no-verify -m "chore(${{ inputs.sdk_name }}): update ${{ inputs.sdk_name }} player version to ${{ inputs.version_number }}" + git push origin ${{ steps.branching.outputs.branch_name }} + + - name: Create PR + run: | + gh pr create \ + --base "${{ github.ref }}" \ + --title "Update ${{ inputs.sdk_name == 'ios' && 'iOS' || inputs.sdk_name }} player to ${{ inputs.version_number }}" \ + --body "Automated ${{ inputs.sdk_name == 'ios' && 'iOS' || inputs.sdk_name }} player version update to ${{ inputs.version_number }}" + + update-v0: + name: Update SDK version on v0 + runs-on: macos-14 + env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: 'development-v0' + + - name: Setup git user + run: | + git config --global user.name "Update Bot" + git config --global user.email "update-bot@bitmovin.com" + + - name: Set update branch name + id: branching + run: | + branch_name="update_${{ inputs.sdk_name }}_player_to_${{ inputs.version_number }}_v0" + echo "branch_name=$branch_name" >> $GITHUB_OUTPUT + + - name: Create update branch + run: | + git push origin --delete ${{ steps.branching.outputs.branch_name }} || true + git checkout -b ${{ steps.branching.outputs.branch_name }} + - name: Setup Environment uses: ./.github/actions/setup-environment with: @@ -53,6 +104,7 @@ jobs: brew: true - name: Restore Pods cache + if: ${{ inputs.sdk_name == 'ios' }} id: pods-cache-restore uses: actions/cache/restore@v3 with: @@ -65,7 +117,7 @@ jobs: - name: Bump iOS player SDK version if: ${{ inputs.sdk_name == 'ios' }} run: | - sed -i '' 's/s.dependency "BitmovinPlayer", ".*/s.dependency "BitmovinPlayer", "${{ inputs.version_number }}"/g' RNBitmovinPlayer.podspec + sed -i '' 's/s\.dependency "BitmovinPlayer", ".*/s.dependency "BitmovinPlayer", "${{ inputs.version_number }}"/g' RNBitmovinPlayer.podspec yarn bootstrap - name: Save Pods cache @@ -84,15 +136,18 @@ jobs: sed -i '' "s/com.bitmovin.player:player:.*/com.bitmovin.player:player:${{ inputs.version_number }}'/g" android/build.gradle sed -i '' "s/com.bitmovin.player:player-media-session:.*/com.bitmovin.player:player-media-session:${{ inputs.version_number }}'/g" android/build.gradle + - name: Update Changelog entry + run: python3 .github/scripts/update_player_sdk_update_changelog.py ${{ inputs.version_number }} ${{ inputs.sdk_name }} + - name: Commit version bump run: | - git add RNBitmovinPlayer.podspec android/build.gradle example/ios/Podfile.lock integration_test/ios/Podfile.lock - git commit -m "chore(${{ inputs.sdk_name }}): update ${{ inputs.sdk_name }} player version to ${{ inputs.version_number }}" + git add RNBitmovinPlayer.podspec android/build.gradle example/ios/Podfile.lock integration_test/ios/Podfile.lock CHANGELOG.md + git commit --no-verify -m "chore(${{ inputs.sdk_name }}): update ${{ inputs.sdk_name }} player version to ${{ inputs.version_number }}" git push origin ${{ steps.branching.outputs.branch_name }} - name: Create PR run: | gh pr create \ - --base "${{ github.ref }}" \ - --title "Update ${{ inputs.sdk_name == 'ios' && 'iOS' || inputs.sdk_name }} player to ${{ inputs.version_number }}" \ - --body "Automated ${{ inputs.sdk_name == 'ios' && 'iOS' || inputs.sdk_name }} player version update to ${{ inputs.version_number }}" + --base "development-v0" \ + --title "Update ${{ inputs.sdk_name == 'ios' && 'iOS' || inputs.sdk_name }} player to ${{ inputs.version_number }} on v0.x.x" \ + --body "Automated ${{ inputs.sdk_name == 'ios' && 'iOS' || inputs.sdk_name }} player version update to ${{ inputs.version_number }} on v0.x.x" diff --git a/.github/workflows/finish-release-train.yml b/.github/workflows/finish-release-train.yml index db1ec643..16ebb279 100644 --- a/.github/workflows/finish-release-train.yml +++ b/.github/workflows/finish-release-train.yml @@ -8,6 +8,7 @@ on: - closed branches: - main + - support/v0 env: LC_ALL: en_US.UTF-8 @@ -49,7 +50,7 @@ jobs: - name: Commit changelog version bump run: | git add CHANGELOG.md - git commit -m "bump changelog date to today" + git commit --no-verify -m "bump changelog date to today" git push origin ${{ github.ref }} create_pr: @@ -60,6 +61,18 @@ jobs: - uses: actions/checkout@v4 - name: Create PR + if: ${{ github.event.pull_request.merged == true && startsWith(github.head_ref, 'support/v0') }} + run: | + gh pr create \ + --base "development-v0" \ + --head "support/v0" \ + --title "Finish release ${{ needs.prepare.outputs.version_number }}" \ + --body "Finish release ${{ needs.prepare.outputs.version_number }}" + env: + GH_TOKEN: ${{ github.token }} + + - name: Create PR + if: ${{ github.event.pull_request.merged == true && !startsWith(github.head_ref, 'support/v0') }} run: | gh pr create \ --base "development" \ diff --git a/.github/workflows/start-release-train.yml b/.github/workflows/start-release-train.yml index 45ba362b..c9c50583 100644 --- a/.github/workflows/start-release-train.yml +++ b/.github/workflows/start-release-train.yml @@ -18,7 +18,6 @@ on: env: LC_ALL: en_US.UTF-8 LANG: en_US.UTF-8 - NO_FLIPPER: 1 concurrency: group: start-release-train-${{ inputs.version_number }} @@ -27,7 +26,7 @@ concurrency: jobs: create_release_pr: name: Create release branch and bump version - runs-on: macos-14 + runs-on: macos-15 outputs: branch_name: ${{ steps.branching.outputs.branch_name }} steps: @@ -44,16 +43,6 @@ jobs: with: node: true - - name: Restore Pods cache - id: pods-cache-restore - uses: actions/cache/restore@v3 - with: - path: | - .cocoapods-cache - example/ios/Pods - key: pods-${{ hashFiles('example/ios/Podfile.lock') }}-${{ hashFiles('integration_test/ios/Podfile.lock') }} - restore-keys: pods- - - name: Set Release Branch name id: branching run: | @@ -72,33 +61,17 @@ jobs: run: | sed -i'.bak' "s/\[Unreleased\]/\[${{ inputs.version_number }}\]/g" CHANGELOG.md awk 'BEGIN {count=0} /## \[/ {count++; if (count == 2) exit} {print}' CHANGELOG.md + rm -f CHANGELOG.md.bak - name: Bump package.json version run: | yarn version --new-version ${{ inputs.version_number }} --no-git-tag-version - - name: Install pods to update Podfile.lock - run: | - yarn bootstrap - env: - CP_HOME_DIR: ${{ github.workspace }}/.cocoapods-cache - NO_FLIPPER: 1 - - - name: Save Pods cache - if: steps.pods-cache-restore.outputs.cache-hit != 'true' - uses: actions/cache/save@v3 - with: - path: | - .cocoapods-cache - example/ios/Pods - integration_test/ios/Pods - key: ${{ steps.pods-cache-restore.outputs.cache-primary-key }} - - name: Commit changelog version bump if: ${{ !inputs.dry_run }} run: | - git add CHANGELOG.md package.json example/ios/Podfile.lock integration_test/ios/Podfile.lock - git commit -m "prepare release ${{ inputs.version_number }}" + git add CHANGELOG.md package.json + git commit --no-verify -m "prepare release ${{ inputs.version_number }}" git push origin ${{ steps.branching.outputs.branch_name }} - name: Create PR diff --git a/.gitignore b/.gitignore index 2d18b882..87b504ad 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,6 @@ # .DS_Store -# XDE -.expo/ - # VSCode .vscode/ jsconfig.json @@ -12,6 +9,7 @@ jsconfig.json # Xcode # build/ +!/build/ *.pbxuser !default.pbxuser *.mode1v3 @@ -28,8 +26,6 @@ DerivedData *.ipa *.xcuserstate project.xcworkspace -*.hprof -.xcode.env.local # Android/IJ # @@ -41,11 +37,16 @@ project.xcworkspace .settings local.properties android.iml +android/app/libs +android/keystores/debug.keystore # Cocoapods # */ios/Pods/ +# Ruby +example/vendor/ + # node.js # node_modules/ @@ -53,20 +54,34 @@ npm-debug.log yarn-debug.log yarn-error.log -# BUCK -buck-out/ -\.buckd/ -android/app/libs -android/keystores/debug.keystore - # Expo .expo/* - -# generated by bob -lib/ - # generated API documentation docs/generated +# Generated native projects (can be regenerated with `yarn example prebuild`) +example/ios/ +example/android/ + # developer specific configuration file example/ios/Developer.xcconfig + +# Expo prebuild +.expo/ +web-build/ + +# Expo local env files +.env*.local + +# EAS +eas-build-pre-install.sh +eas-build-post-install.sh +eas-hooks/ + +# Plugin build artifacts +plugin/tsconfig.tsbuildinfo +plugin/build/ + +example/.env + +.docs diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index 66ac4936..00000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -if [ "$(uname -m)" = arm64 ]; then - export PATH="/opt/homebrew/bin:$PATH" -fi - -yarn lint-staged - -if [ "$(uname)" = Darwin ]; then - swiftlint lint --strict -fi - -(cd android && ./gradlew ktlintFormat) diff --git a/.npmignore b/.npmignore index 952a2e20..8a981c9c 100644 --- a/.npmignore +++ b/.npmignore @@ -1,69 +1,15 @@ -# Built application files -android/*/build/ +# Exclude all top-level hidden directories by convention +/.*/ -# Crashlytics configuations -android/com_crashlytics_export_strings.xml +# Exclude tarballs generated by `npm pack` +/*.tgz -# Local configuration file (sdk path, etc) -android/local.properties +__mocks__ +__tests__ -# Gradle generated files -android/.gradle/ - -# Signing files -android/.signing/ - -# User-specific configurations -android/.idea/gradle.xml -android/.idea/libraries/ -android/.idea/workspace.xml -android/.idea/tasks.xml -android/.idea/.name -android/.idea/compiler.xml -android/.idea/copyright/profiles_settings.xml -android/.idea/encodings.xml -android/.idea/misc.xml -android/.idea/modules.xml -android/.idea/scopes/scope_settings.xml -android/.idea/vcs.xml -android/*.iml - -# Xcode -*.pbxuser -*.mode1v3 -*.mode2v3 -*.perspectivev3 -*.xcuserstate -ios/Pods -ios/build -*project.xcworkspace* -*xcuserdata* - -# OS-specific files -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.dbandroid/gradle -android/gradlew -android/build -android/gradlew.bat -android/gradle/ - -.idea -coverage -yarn.lock -e2e/ -.husky -.github -.vscode -.nyc_output -android/.settings -*.coverage.json -.circleci -.eslintignore -type-test.ts -example/ +/babel.config.js +/android/src/androidTest/ +/android/src/test/ +/android/build/ +/example/ integration_test/ diff --git a/.prettierignore b/.prettierignore index f0a0c196..afcc2d75 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,9 +1,11 @@ lib/ node_modules/ example/node_modules/ +example/android/ example/ios/ example/ios/Pods/ integration_test/node_modules/ +integration_test/android/ integration_test/ios/ integration_test/ios/Pods/ .github/*.md diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 00000000..6cb9d3dd --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.4.3 diff --git a/.swiftlint.yml b/.swiftlint.yml index 277237e8..7d98680d 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -2,9 +2,11 @@ excluded: # folders that are excluded from linting, external dependencies or generated files - ${PWD}/node_modules - ${PWD}/example/ios/Pods + - ${PWD}/example/ios - ${PWD}/example/node_modules - ${PWD}/integration_test/ios/Pods - ${PWD}/integration_test/node_modules + - ${PWD}/.cocoapods-cache disabled_rules: - todo diff --git a/Brewfile.lock.json b/Brewfile.lock.json deleted file mode 100644 index ed6c3e1b..00000000 --- a/Brewfile.lock.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "entries": { - "brew": { - "swiftlint": { - "version": "0.55.1", - "bottle": { - "rebuild": 1, - "root_url": "https://ghcr.io/v2/homebrew/core", - "files": { - "arm64_sonoma": { - "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:0610290fef665ecfc022ec3e8e3986224841290274a21efdee503e76b7b39bcc", - "sha256": "0610290fef665ecfc022ec3e8e3986224841290274a21efdee503e76b7b39bcc" - }, - "arm64_ventura": { - "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:af05ed001b0476ed0391778516ed92cc3ed100593a03794025c1814e6dec0cb4", - "sha256": "af05ed001b0476ed0391778516ed92cc3ed100593a03794025c1814e6dec0cb4" - }, - "arm64_monterey": { - "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:2e1313a57188d5a751f038596509f0186a058669abfd21aa3142457f9c16c478", - "sha256": "2e1313a57188d5a751f038596509f0186a058669abfd21aa3142457f9c16c478" - }, - "sonoma": { - "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:148e407d81cedffbe76876288f78a35fee69f82d12b2fb3356ca102c2fd6d319", - "sha256": "148e407d81cedffbe76876288f78a35fee69f82d12b2fb3356ca102c2fd6d319" - }, - "ventura": { - "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:709d73d12dd3adf64e276b04e94949749b5073f7ca946e0ead585557cfe9277c", - "sha256": "709d73d12dd3adf64e276b04e94949749b5073f7ca946e0ead585557cfe9277c" - }, - "monterey": { - "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:30ea7b1e56634ccd521ffe86a93372e02004cf25b0a10432a5b54520a71d4139", - "sha256": "30ea7b1e56634ccd521ffe86a93372e02004cf25b0a10432a5b54520a71d4139" - }, - "x86_64_linux": { - "cellar": "/home/linuxbrew/.linuxbrew/Cellar", - "url": "https://ghcr.io/v2/homebrew/core/swiftlint/blobs/sha256:0f68576b2b4591e126e923278c4aa25c28aa18b7e5a9f8a3b8d7cf8eeacfe3a2", - "sha256": "0f68576b2b4591e126e923278c4aa25c28aa18b7e5a9f8a3b8d7cf8eeacfe3a2" - } - } - } - }, - "xcbeautify": { - "version": "2.4.0", - "bottle": { - "rebuild": 0, - "root_url": "https://ghcr.io/v2/homebrew/core", - "files": { - "arm64_sonoma": { - "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/xcbeautify/blobs/sha256:6a51e17f553aa9d493bf806489fa506c178fed38b18b45a94cd6cbb8a50ed042", - "sha256": "6a51e17f553aa9d493bf806489fa506c178fed38b18b45a94cd6cbb8a50ed042" - }, - "arm64_ventura": { - "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/xcbeautify/blobs/sha256:9717191935a921a937474cc785728238efe4cbea9d590d97927f4f9c990d5e6b", - "sha256": "9717191935a921a937474cc785728238efe4cbea9d590d97927f4f9c990d5e6b" - }, - "sonoma": { - "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/xcbeautify/blobs/sha256:61a163991b33aa679931917fb1952aa9d6c3b3bf57c5637c24be89da513cb49f", - "sha256": "61a163991b33aa679931917fb1952aa9d6c3b3bf57c5637c24be89da513cb49f" - }, - "ventura": { - "cellar": ":any_skip_relocation", - "url": "https://ghcr.io/v2/homebrew/core/xcbeautify/blobs/sha256:08bdbf866a823ba2d27db510eaaf191d0c2a87a69687b86c5efa96e546f43d51", - "sha256": "08bdbf866a823ba2d27db510eaaf191d0c2a87a69687b86c5efa96e546f43d51" - }, - "x86_64_linux": { - "cellar": "/home/linuxbrew/.linuxbrew/Cellar", - "url": "https://ghcr.io/v2/homebrew/core/xcbeautify/blobs/sha256:a157d806c66671dec34bd0dc3dd7ae5fe193e59a527579db79ba7d3fe1824770", - "sha256": "a157d806c66671dec34bd0dc3dd7ae5fe193e59a527579db79ba7d3fe1824770" - } - } - } - } - } - }, - "system": { - "macos": { - "sonoma": { - "HOMEBREW_VERSION": "4.1.20", - "HOMEBREW_PREFIX": "/opt/homebrew", - "Homebrew/homebrew-core": "api", - "CLT": "", - "Xcode": "15.0.1", - "macOS": "14.0" - }, - "sequoia": { - "HOMEBREW_VERSION": "4.3.8", - "HOMEBREW_PREFIX": "/opt/homebrew", - "Homebrew/homebrew-core": "api", - "CLT": "16.0.0.0.1.1719078471", - "Xcode": "16.0", - "macOS": "15.0" - } - } - } -} diff --git a/CHANGELOG.md b/CHANGELOG.md index 00311812..e5a49441 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,103 @@ # Changelog -## [Unreleased] +## [1.1.0] - 2025-09-03 + +### Changed + +- Update Bitmovin's native Android SDK version to `3.123.0` +- Update Bitmovin's native iOS SDK version to `3.94.1` + +### Fixed + +- Crash when using `react-native-reanimated` along with `bitmovin-player-react-native` and playing a live stream +- Expo config plugin feature configurations taking no effect +- Example application crash on tvOS simulator + +## [1.0.0] - 2025-08-04 + +### Breaking Change + +- Introduction of Expo SDK support. Upgrading requires following the [Migration Guide](https://developer.bitmovin.com/playback/docs/react-native-migrating-to-v1). + +### Added + +- React Native New Architecture Support +- Expo Config Plugin to manage native configuration from `app.config.ts`. +- Automatic configuration for Google Cast, Offline, Picture-in-Picture, AirPlay, and Background Playback through Expo plugin. + +### Changed + +- Minimum iOS/tvOS version is now 15.1+ (was 14.0+). Due to a transient React Native minimum [iOS/tvOS version change](https://github.com/react-native-community/discussions-and-proposals/discussions/812). +- Minimum Android SDK version is now 24 (was 21). Due to a transient React Native minimum [Android version change](https://github.com/react-native-community/discussions-and-proposals/discussions/802). +- Native setup is now automated through Expo SDK - manual configuration is no longer required for v1.0.0+. + +## [0.44.0] - 2025-07-25 + +### Changed + +- Update Bitmovin's native Android SDK version to `3.118.0` +- Update Bitmovin's native iOS SDK version to `3.93.0` +- Android: Add null safety checks to `ReadableMap`/`ReadableArray` calls in `JsonConverter` + +### Removed + +- Android: `TweaksConfig.shouldApplyTtmlRegionWorkaround` as support for TTML attributes defined in a Region's Style has improved + +## [0.43.0] - 2025-06-30 + +### Added + +- Android: `AudioTrack.qualities`, providing the `AudioQuality`s associated with the `AudioTrack` + +### Changed + +- Update Bitmovin's native Android SDK version to `3.115.0` +- Update Bitmovin's native iOS SDK version to `3.92.0` + +## [0.42.0] - 2025-06-02 + +### Changed + +- Update Bitmovin's native Android SDK version to `3.112.0` +- Update Bitmovin's native iOS SDK version to `3.90.0` + +### Added + +- `SubtitleTrack.roles` and `AudioTrack.roles` to list the associated `MediaTrackRole` information + +## [0.41.0] - 2025-04-02 + +### Changed + +- Update react-native-screens to v3.35.0 for Android SDK version compatibility + +### Added + +- Android: `DecoderConfig.decoderPriorityProvider`, a callback interface to specify which decoder implementation the Player should use to decode the media + +### Changed + +- Update IMA SDK dependency on Android to `3.35.1` + +## [0.40.0] - 2025-03-20 + +### Changed + +- Update Bitmovin's native Android SDK version to `3.104.2` +- Update Bitmovin's native iOS SDK version to `3.85.2` + +## [0.39.0] - 2025-03-07 + +### Added + +- `CueEnterEvent.image` and `CueExitEvent.image` to expose the Base64 encoded image data URI of the cue when available + +## [0.38.0] - 2025-02-28 + +### Changed + +- Update Bitmovin's native Android SDK version to `3.104.1` +- Update Bitmovin's native iOS SDK version to `3.85.0` ## [0.37.0] - 2025-01-17 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b6743767..cd02101b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,17 +41,14 @@ To build and run the example app on iOS: yarn example ios ``` -To edit the Swift/Objective-C files, open `example/ios/BitmovinPlayerReactNativeExample.xcworkspace` in Xcode and find the source files at `Pods > Development Pods > RNBitmovinPlayer`. +To edit the Swift/Objective-C files, open Xcode via `yarn example open:ios` and find the source files at `Pods > Development Pods > RNBitmovinPlayer`. -To edit the Kotlin files, open `example/android` in Android Studio and find the source files at `bitmovin-player-react-native` under `Android`. +To edit the Kotlin files, open Android Studio via `yarn example open:android` and find the source files at `bitmovin-player-react-native` under `Android`. -## For iOS/tvOS on-device development +## Development setup -To build the example project for an iOS or tvOS device, you need to create a file at `example/ios/Developer.xcconfig`. In this file, add your development team like this: - -```yml -DEVELOPMENT_TEAM = YOUR_TEAM_ID -``` +- For the Example app, see the relevant [`example/README`](example/README.md#development-setup) section. +- For the integration tests, see the relevant [`integration_test/README`](README.md#2-environment-configuration) section. ## TypeScript Code Style @@ -63,69 +60,111 @@ DEVELOPMENT_TEAM = YOUR_TEAM_ID ## Linting +### Pre-commit Hooks + +The project uses pre-commit hooks to automatically enforce code quality standards across all languages (TypeScript, Swift, Kotlin). The hooks will: + +- Run ESLint (quiet mode) on TypeScript/JavaScript files +- Auto-format Swift files with SwiftLint, then run SwiftLint (strict mode) +- Auto-format Kotlin files with ktlint, then run ktlint +- Auto-format files with Prettier + +**Setup:** + +```sh +yarn setup-hooks +``` + +Or manually install the pre-commit hook: + +```sh +# Copy the pre-commit hook (done automatically by yarn setup-hooks) +cp scripts/pre-commit.sh .git/hooks/pre-commit +chmod +x .git/hooks/pre-commit +``` + +**Testing all linting:** + +```sh +yarn lint:all +``` + ### Typescript [ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/) -We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing. +We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code. -Our pre-commit hooks verify that the linter will pass when committing. Make sure your code passes TypeScript and ESLint. Run the following to verify: ```sh -yarn typescript +yarn typecheck yarn lint ``` -To fix formatting errors, run the following: +To run TypeScript checking for all packages: ```sh -yarn lint --fix +yarn typecheck:all ``` -### Kotlin +To fix formatting errors, run the following: -For Kotlin code [ktlint](https://pinterest.github.io/ktlint/) is used with [ktlint gradle plugin](https://github.com/jlleitschuh/ktlint-gradle). -Run the following inside `android` folder to verify code format: +```sh +yarn format:all +``` + +Or for specific platforms: ```sh -./gradlew ktlintCheck +yarn format # Prettier (TypeScript/JavaScript/Markdown/JSON/YAML) +yarn format:ios # SwiftLint auto-correction +yarn format:android # ktlint formatting ``` -To fix formatting errors, run the following inside `android` folder: +### Kotlin + +For Kotlin code [ktlint](https://pinterest.github.io/ktlint/) is used with [ktlint gradle plugin](https://github.com/jlleitschuh/ktlint-gradle). + +Run the following to verify code format: ```sh -./gradlew ktlintFormat +yarn lint:android ``` -You can add a lint check pre-commit hook by running inside `android` folder: +To fix formatting errors, run the following: ```sh -./gradlew addKtlintCheckGitPreCommitHook +yarn format:android ``` -and for automatic pre-commit formatting: +Or manually inside `android` folder: ```sh -./gradlew addKtlintFormatGitPreCommitHook +./gradlew ktlintFormat ``` ### Swift For Swift code [SwiftLint](https://github.com/realm/SwiftLint) is used. To install SwiftLint, run `brew bundle install` in the root directory. -Our pre-commit hooks verify that the linter will pass when committing. To verify Swift code, run the following: ```sh -swiftlint +yarn lint:ios ``` To fix auto-fixable SwiftLint violations, run the following: ```sh -swiftlint lint --autocorrect +yarn format:ios +``` + +Or manually: + +```sh +swiftlint ios --autocorrect ``` ## Testing @@ -170,12 +209,15 @@ export default (spec: TestScope) => { spec.it('emits TimeChanged events', async () => { await startPlayerTest({}, async () => { await loadSourceConfig({ - url: 'https://cdn.bitmovin.com/content/assets/MI201109210084/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8', + url: 'https://cdn.bitmovin.com/content/internal/assets/MI201109210084/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8', type: SourceType.HLS, }); - await callPlayerAndExpectEvents((player) => { - player.play(); - }, EventSequence(EventType.Play, EventType.Playing)); + await callPlayerAndExpectEvents( + (player) => { + player.play(); + }, + EventSequence(EventType.Play, EventType.Playing) + ); await expectEvents(RepeatedEvent(EventType.TimeChanged, 5)); }); }); @@ -191,9 +233,13 @@ The `package.json` file contains various scripts for common tasks: - `yarn bootstrap:example`: setup example project by installing all dependencies and pods. - `yarn bootstrap:integration-test`: setup integration tests project by installing all dependencies and pods. - `yarn build`: compile TypeScript files into `lib/` with ESBuild. -- `yarn typescript`: type-check files with TypeScript. +- `yarn typecheck`: type-check files with TypeScript. +- `yarn typecheck:all`: type-check files with TypeScript in all packages. - `yarn lint`: lint files with ESLint. - `yarn format`: format files with Prettier. +- `yarn format:ios`: auto-fix SwiftLint violations. +- `yarn format:android`: format Kotlin files with ktlint. +- `yarn format:all`: format all files (Prettier, SwiftLint, ktlint). - `yarn docs`: generate documentation with TypeDoc. - `yarn brew`: install all dependencies for iOS development with Homebrew. - `yarn example start`: start the Metro server for the example app. diff --git a/README.md b/README.md index 49a67549..28718e9c 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,12 @@ This is an open-source project created to enable customers to integrate the Bitm ## Platform Support -This library requires at least React Native 0.65+ and React 17+ to work properly. The **officially supported** platforms are: +This library requires at least Expo 53+, React Native 0.79+ and React 17+ to work properly. The **officially supported** platforms are: -- **iOS/iPadOS/tvOS:** 14.0+ -- **Android:** 5.0+ +- **iOS/iPadOS/tvOS:** 15.1+ +- **Android:** 7.0+ - **Android TV:** 7+ -- **Fire TV:** Fire OS 6.0+ ([compatible](https://developer.bitmovin.com/playback/docs/supported-platforms-devices-player#support-levels) with Fire OS 5.0) +- **Fire TV:** Fire OS 6.0+ Please note that browsers and other browser-like environments such as webOS and Tizen are not supported. For more details regarding Bitmovin Player SDK platform and device support, please refer to the [Supported Platforms & Devices](https://developer.bitmovin.com/playback/docs/supported-platforms-devices-player) page of our documentation. diff --git a/RNBitmovinPlayer.podspec b/RNBitmovinPlayer.podspec deleted file mode 100644 index 2ac8df15..00000000 --- a/RNBitmovinPlayer.podspec +++ /dev/null @@ -1,26 +0,0 @@ -require "json" - -package = JSON.parse(File.read(File.join(__dir__, "package.json"))) - -Pod::Spec.new do |s| - s.name = "RNBitmovinPlayer" - s.version = package["version"] - s.summary = package["description"] - s.homepage = package["homepage"] - s.license = package["license"] - s.authors = package["author"] - - s.platforms = { :ios => "14.0", :tvos => "14.0" } - s.source = { - :git => "https://github.com/bitmovin/bitmovin-player-react-native.git", - :tag => "v#{s.version}" - } - - s.source_files = "ios/**/*.{h,m,mm,swift}" - - s.swift_version = "5.10" - s.dependency "React-Core" - s.dependency "BitmovinPlayer", "3.81.0" - s.ios.dependency "GoogleAds-IMA-iOS-SDK", "3.23.0" - s.tvos.dependency "GoogleAds-IMA-tvOS-SDK", "4.13.0" -end diff --git a/android/build.gradle b/android/build.gradle index 44600b8e..30d46660 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,8 +1,15 @@ +import groovy.json.JsonSlurper + +// Read version from package.json +def packageJsonFile = new File("$rootDir/../package.json") +def packageJson = new JsonSlurper().parseText(packageJsonFile.text) +def packageVersion = packageJson.version + buildscript { // Buildscript is evaluated before everything else so we can't use getExtOrDefault def kotlin_version = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : project.properties["BitmovinPlayerReactNative_kotlinVersion"] def ktlint_version = rootProject.ext.has("ktlintVersion") ? rootProject.ext.get("ktlintVersion") : project.properties["BitmovinPlayerReactNative_ktlintVersion"] - def android_tools_version = rootProject.ext.has("androidToolsVersion") ? rootProject.ext.get("androidToolsVersion") : project.properties["BitmovinPlayerReactNative_androidToolsVersion"] + def android_plugin_version = rootProject.ext.has("androidPluginVersion") ? rootProject.ext.get("androidPluginVersion") : project.properties["BitmovinPlayerReactNative_androidPluginVersion"] repositories { google() @@ -12,99 +19,95 @@ buildscript { } } dependencies { - classpath "com.android.tools.build:gradle:$android_tools_version" - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "com.android.tools.build:gradle:$android_plugin_version" classpath "org.jlleitschuh.gradle:ktlint-gradle:$ktlint_version" } } -def isNewArchitectureEnabled() { - return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" -} - apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' apply plugin: 'org.jlleitschuh.gradle.ktlint' -if (isNewArchitectureEnabled()) { - apply plugin: "com.facebook.react" -} - -def getExtOrDefault(name) { - return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["BitmovinPlayerReactNative_" + name] -} - -def getExtOrIntegerDefault(name) { - return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["BitmovinPlayerReactNative_" + name]).toInteger() -} - -def supportsNamespace() { - def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.') - def major = parsed[0].toInteger() - def minor = parsed[1].toInteger() - - // Namespace support was added in 7.3.0 - return (major == 7 && minor >= 3) || major >= 8 -} - -android { - if (supportsNamespace()) { - namespace "com.bitmovin.player.reactnative" - - sourceSets { - main { - manifest.srcFile "src/main/AndroidManifestNew.xml" - } - } +group = 'com.bitmovin.player.reactnative' +version = packageVersion + +def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle") +apply from: expoModulesCorePlugin +applyKotlinExpoModulesCorePlugin() +useCoreDependencies() +useExpoPublishing() + +// If you want to use the managed Android SDK versions from expo-modules-core, set this to true. +// The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code. +// Most of the time, you may like to manage the Android SDK versions yourself. +def useManagedAndroidSdkVersions = false +if (useManagedAndroidSdkVersions) { + useDefaultAndroidSdkVersions() +} else { + buildscript { + // Simple helper that allows the root project to override versions declared by this library. + ext.safeExtGet = { prop, fallback -> + rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback } - - compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") - + } + project.android { + compileSdkVersion safeExtGet("compileSdkVersion", 34) defaultConfig { - minSdkVersion getExtOrIntegerDefault("minSdkVersion") - targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") - + minSdkVersion safeExtGet("minSdkVersion", 24) + targetSdkVersion safeExtGet("targetSdkVersion", 35) } + } +} - buildTypes { - release { - minifyEnabled false - } +android { + namespace "com.bitmovin.player.reactnative" + + defaultConfig { + versionCode 1 + versionName packageVersion + } + + buildTypes { + release { + minifyEnabled false } + } - lintOptions { - disable "GradleCompatible" - } + lintOptions { + disable "GradleCompatible" + abortOnError false + } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } - buildFeatures { - buildConfig true - } + buildFeatures { + buildConfig true + } } repositories { - google() - mavenCentral() + google() + mavenCentral() + maven { + url "https://artifacts.bitmovin.com/artifactory/public-releases" + } } -def kotlin_version = getExtOrDefault("kotlinVersion") - dependencies { - // For < 0.71, this will be from the local maven repo - // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin - //noinspection GradleDynamicVersion - implementation 'com.facebook.react:react-native:+' // From node_modules - implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "androidx.concurrent:concurrent-futures:1.1.0" - implementation "androidx.concurrent:concurrent-futures-ktx:1.1.0" - - // Bitmovin - implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.33.0' - implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1' - implementation 'com.bitmovin.player:player:3.98.0+jason' - implementation 'com.bitmovin.player:player-media-session:3.98.0+jason' + // React Native (needed for hybrid bridge modules) + //noinspection GradleDynamicVersion + implementation 'com.facebook.react:react-native:+' // From node_modules + + implementation "androidx.concurrent:concurrent-futures:1.1.0" + implementation "androidx.concurrent:concurrent-futures-ktx:1.1.0" + + // Google IMA + implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.35.1' + + // Bitmovin + implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1' + implementation 'com.bitmovin.player:player:3.123.0+jason' + implementation 'com.bitmovin.player:player-media-session:3.123.0+jason' } diff --git a/android/gradle.properties b/android/gradle.properties index c6366aed..f6731507 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,9 +1,11 @@ android.useAndroidX=true +# Also update example/android/gradle.properties and integration_test/android/gradle.properties when updating kotlin version. BitmovinPlayerReactNative_kotlinVersion=1.9.21 -BitmovinPlayerReactNative_minSdkVersion=21 -BitmovinPlayerReactNative_targetSdkVersion=34 -BitmovinPlayerReactNative_compileSdkVersion=34 +BitmovinPlayerReactNative_minSdkVersion=24 +BitmovinPlayerReactNative_targetSdkVersion=35 +BitmovinPlayerReactNative_compileSdkVersion=35 BitmovinPlayerReactNative_buildToolsVersion=34.0.0 BitmovinPlayerReactNative_ndkversion=25.1.8937393 BitmovinPlayerReactNative_androidToolsVersion=8.1.0 +BitmovinPlayerReactNative_androidPluginVersion=8.1.0 BitmovinPlayerReactNative_ktlintVersion=11.6.0 diff --git a/android/ktlint.gradle b/android/ktlint.gradle new file mode 100644 index 00000000..c8c11b24 --- /dev/null +++ b/android/ktlint.gradle @@ -0,0 +1,31 @@ +buildscript { + ext { + ktlint_version = project.hasProperty("BitmovinPlayerReactNative_ktlintVersion") ? project.property("BitmovinPlayerReactNative_ktlintVersion") : "11.6.0" + } + repositories { + google() + mavenCentral() + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath "org.jlleitschuh.gradle:ktlint-gradle:$ktlint_version" + } +} + +repositories { + google() + mavenCentral() + maven { + url "https://plugins.gradle.org/m2/" + } +} + +apply plugin: 'org.jlleitschuh.gradle.ktlint' + +ktlint { + android = true + outputColorName = "RED" + ignoreFailures = false +} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/ActivityLifecycleListener.kt b/android/src/main/java/com/bitmovin/player/reactnative/ActivityLifecycleListener.kt new file mode 100644 index 00000000..74130001 --- /dev/null +++ b/android/src/main/java/com/bitmovin/player/reactnative/ActivityLifecycleListener.kt @@ -0,0 +1,51 @@ +package com.bitmovin.player.reactnative + +import android.app.Activity +import android.content.Context +import android.os.Bundle +import android.util.Log +import expo.modules.core.interfaces.ReactActivityLifecycleListener +import java.util.concurrent.Executors + +class ActivityLifecycleListener : ReactActivityLifecycleListener { + override fun onCreate(activity: Activity, savedInstanceState: Bundle?) { + maybeInitializeCastContext(activity) + } + + private fun maybeInitializeCastContext(context: Context) { + // Only initialize CastContext if GoogleCast is configured via Expo plugin + if (!isCastConfigured(context)) { + return + } + + try { + val castContextClass = Class.forName("com.google.android.gms.cast.framework.CastContext") + val getSharedInstanceMethod = castContextClass.getMethod( + "getSharedInstance", + Context::class.java, + java.util.concurrent.Executor::class.java, + ) + val executor = Executors.newSingleThreadExecutor() + + // The method returns a Task, but we don't need to wait for it + // The initialization will happen asynchronously + getSharedInstanceMethod.invoke(null, context, executor) + } catch (e: ClassNotFoundException) { + // GoogleCast SDK not included in build - this is expected when not configured + } catch (e: NoSuchMethodException) { + Log.w("ActivityLifecycleListener", "GoogleCast SDK version incompatible: ${e.message}") + } catch (e: Exception) { + Log.w("ActivityLifecycleListener", "Failed to initialize GoogleCast: ${e.message}") + } + } + + private fun isCastConfigured(context: Context): Boolean { + return try { + val packageManager = context.packageManager + val appInfo = packageManager.getApplicationInfo(context.packageName, 128) + appInfo.metaData?.getString("com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME") != null + } catch (e: Exception) { + false + } + } +} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/BitmovinBaseModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/BitmovinBaseModule.kt deleted file mode 100644 index 145f455b..00000000 --- a/android/src/main/java/com/bitmovin/player/reactnative/BitmovinBaseModule.kt +++ /dev/null @@ -1,110 +0,0 @@ -package com.bitmovin.player.reactnative - -import android.util.Log -import com.bitmovin.player.api.Player -import com.bitmovin.player.api.source.Source -import com.bitmovin.player.reactnative.extensions.drmModule -import com.bitmovin.player.reactnative.extensions.networkModule -import com.bitmovin.player.reactnative.extensions.offlineModule -import com.bitmovin.player.reactnative.extensions.playerModule -import com.bitmovin.player.reactnative.extensions.sourceModule -import com.bitmovin.player.reactnative.extensions.uiManagerModule -import com.facebook.react.bridge.* -import com.facebook.react.uimanager.UIManagerModule - -private const val MODULE_NAME = "BitmovinBaseModule" - -/** - * Base for Bitmovin React modules. - * - * Provides many helper methods that are promise exception safe. - * - * In general, code should not throw while resolving a [Promise]. Instead, [Promise.reject] should be used. - * This doesn't match Kotlin's error style, which uses exception. The helper methods in this class, provide such - * convenience, they can only be called in a context that will catch any Exception and reject the [Promise]. - * - */ -abstract class BitmovinBaseModule( - protected val context: ReactApplicationContext, -) : ReactContextBaseJavaModule(context) { - /** - * Runs [block] on the UI thread with [UIManagerModule.addUIBlock] and [TPromise.resolve] [this] with - * its return value. If [block] throws, [Promise.reject] [this] with the [Throwable]. - */ - protected inline fun TPromise.resolveOnUiThread( - crossinline block: RejectPromiseOnExceptionBlock.() -> R, - ) { - val uiManager = runAndRejectOnException { uiManager } ?: return - uiManager.addUIBlock { - resolveOnCurrentThread { block() } - } - } - - protected val RejectPromiseOnExceptionBlock.playerModule: PlayerModule get() = context.playerModule - ?: throw IllegalArgumentException("PlayerModule not found") - - protected val RejectPromiseOnExceptionBlock.uiManager: UIManagerModule get() = context.uiManagerModule - ?: throw IllegalStateException("UIManager not found") - - protected val RejectPromiseOnExceptionBlock.sourceModule: SourceModule get() = context.sourceModule - ?: throw IllegalStateException("SourceModule not found") - - protected val RejectPromiseOnExceptionBlock.offlineModule: OfflineModule get() = context.offlineModule - ?: throw IllegalStateException("OfflineModule not found") - - protected val RejectPromiseOnExceptionBlock.drmModule: DrmModule get() = context.drmModule - ?: throw IllegalStateException("DrmModule not found") - - protected val RejectPromiseOnExceptionBlock.networkModule: NetworkModule get() = context.networkModule - ?: throw IllegalStateException("NetworkModule not found") - - fun RejectPromiseOnExceptionBlock.getPlayer( - nativeId: NativeId, - playerModule: PlayerModule = this.playerModule, - ): Player = playerModule.getPlayerOrNull(nativeId) ?: throw IllegalArgumentException("Invalid PlayerId $nativeId") - - fun RejectPromiseOnExceptionBlock.getSource( - nativeId: NativeId, - sourceModule: SourceModule = this.sourceModule, - ): Source = sourceModule.getSourceOrNull(nativeId) ?: throw IllegalArgumentException("Invalid SourceId $nativeId") -} - -/** Run [block], returning it's return value. If [block] throws, [Promise.reject] [this] and return null. */ -inline fun TPromise.runAndRejectOnException(block: RejectPromiseOnExceptionBlock.() -> R): R? = try { - RejectPromiseOnExceptionBlock.block() -} catch (e: Exception) { - reject(e) - null -} - -/** - * [TPromise.resolve] [this] with [block] return value. - * If [block] throws, [Promise.reject] [this] with the [Throwable]. - */ -inline fun TPromise.resolveOnCurrentThread( - crossinline block: RejectPromiseOnExceptionBlock.() -> T, -): Unit = runAndRejectOnException { this@resolveOnCurrentThread.resolve(block()) } ?: Unit - -/** Receiver of code that can safely throw when resolving a [Promise]. */ -object RejectPromiseOnExceptionBlock - -/** Compile time wrapper for Promises to type check the resolved type [T]. */ -@JvmInline -value class TPromise(val promise: Promise) { - // Promise only support built-in types. Functions that return [Unit] must resolve to `null`. - fun resolve(value: T): Unit = promise.resolve(value.takeUnless { it is Unit }) - fun reject(throwable: Throwable) { - Log.e(MODULE_NAME, "Failed to execute Bitmovin method", throwable) - promise.reject(throwable) - } -} - -inline val Promise.int get() = TPromise(this) -inline val Promise.unit get() = TPromise(this) -inline val Promise.string get() = TPromise(this) -inline val Promise.double get() = TPromise(this) -inline val Promise.float get() = TPromise(this) -inline val Promise.bool get() = TPromise(this) -inline val Promise.map get() = TPromise(this) -inline val Promise.array get() = TPromise(this) -inline val TPromise.nullable get() = TPromise(promise) diff --git a/android/src/main/java/com/bitmovin/player/reactnative/BitmovinCastManagerModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/BitmovinCastManagerModule.kt index 6d9cfbd4..368305ce 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/BitmovinCastManagerModule.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/BitmovinCastManagerModule.kt @@ -1,60 +1,37 @@ package com.bitmovin.player.reactnative import com.bitmovin.player.casting.BitmovinCastManager -import com.bitmovin.player.reactnative.converter.toCastOptions -import com.facebook.react.bridge.Promise -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactMethod -import com.facebook.react.bridge.ReadableMap -import com.facebook.react.module.annotations.ReactModule - -private const val MODULE_NAME = "BitmovinCastManagerModule" - -@ReactModule(name = MODULE_NAME) -class BitmovinCastManagerModule(context: ReactApplicationContext) : BitmovinBaseModule(context) { - override fun getName() = MODULE_NAME - - /** - * Returns whether the [BitmovinCastManager] is initialized. - */ - @ReactMethod - fun isInitialized(promise: Promise) = promise.unit.resolveOnUiThread { - BitmovinCastManager.isInitialized() - } - - /** - * Initializes the [BitmovinCastManager] with the given options. - */ - @ReactMethod - fun initializeCastManager(options: ReadableMap?, promise: Promise) = promise.unit.resolveOnUiThread { - val castOptions = options?.toCastOptions() - BitmovinCastManager.initialize( - castOptions?.applicationId, - castOptions?.messageNamespace, - ) - } - - /** - * Sends a message to the receiver. - */ - @ReactMethod - fun sendMessage(message: String, messageNamespace: String?, promise: Promise) = promise.unit.resolveOnUiThread { - BitmovinCastManager.getInstance().sendMessage(message, messageNamespace) - } - - /** - * Updates the context of the [BitmovinCastManager] to the current activity. - */ - @ReactMethod - fun updateContext(promise: Promise) = promise.unit.resolveOnUiThread { - BitmovinCastManager.getInstance().updateContext(currentActivity) +import com.bitmovin.player.reactnative.extensions.getString +import expo.modules.kotlin.functions.Queues +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition + +class BitmovinCastManagerModule : Module() { + override fun definition() = ModuleDefinition { + Name("BitmovinCastManagerModule") + + AsyncFunction("isInitialized") { + BitmovinCastManager.isInitialized() + } + + AsyncFunction("initializeCastManager") { options: Map? -> + val applicationId = options?.getString("applicationId") + val messageNamespace = options?.getString("messageNamespace") + + BitmovinCastManager.initialize( + applicationId, + messageNamespace, + ) + }.runOnQueue(Queues.MAIN) + + AsyncFunction("sendMessage") { message: String, messageNamespace: String? -> + BitmovinCastManager.getInstance().sendMessage(message, messageNamespace) + }.runOnQueue(Queues.MAIN) + + AsyncFunction("updateContext") { + appContext.currentActivity?.let { activity -> + BitmovinCastManager.getInstance().updateContext(activity) + } + }.runOnQueue(Queues.MAIN) } } - -/** - * Represents configuration options for the [BitmovinCastManager]. - */ -data class BitmovinCastManagerOptions( - val applicationId: String? = null, - val messageNamespace: String? = null, -) diff --git a/android/src/main/java/com/bitmovin/player/reactnative/BufferModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/BufferModule.kt index a106ef10..23e10bcf 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/BufferModule.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/BufferModule.kt @@ -1,55 +1,40 @@ package com.bitmovin.player.reactnative -import com.bitmovin.player.api.buffer.BufferLevel +import com.bitmovin.player.api.buffer.BufferType import com.bitmovin.player.api.media.MediaType -import com.bitmovin.player.reactnative.converter.toBufferType +import com.bitmovin.player.reactnative.converter.toBufferTypeOrThrow import com.bitmovin.player.reactnative.converter.toJson -import com.facebook.react.bridge.* -import com.facebook.react.module.annotations.ReactModule - -private const val MODULE_NAME = "BufferModule" -private const val INVALID_BUFFER_TYPE = "Invalid buffer type" - -@ReactModule(name = MODULE_NAME) -class BufferModule(context: ReactApplicationContext) : BitmovinBaseModule(context) { - override fun getName() = MODULE_NAME - - /** - * Gets the [BufferLevel] from the Player - * @param nativeId Target player id. - * @param type The [type of buffer][toBufferType] to return the level for. - * @param promise JS promise object. - */ - @ReactMethod - fun getLevel(nativeId: NativeId, type: String, promise: Promise) { - promise.map.resolveOnUiThread { - val player = getPlayer(nativeId) +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition + +class BufferModule : Module() { + override fun definition() = ModuleDefinition { + Name("BufferModule") + + OnCreate { + // Module initialization + } + + AsyncFunction("getLevel") { playerId: String, type: String -> + val player = appContext.registry.getModule()?.getPlayerOrNull(playerId) + ?: return@AsyncFunction null + val bufferType = type.toBufferTypeOrThrow() - RNBufferLevels( - audio = player.buffer.getLevel(bufferType, MediaType.Audio), - video = player.buffer.getLevel(bufferType, MediaType.Video), - ).toJson() + val audioLevel = player.buffer.getLevel(bufferType, MediaType.Audio) + val videoLevel = player.buffer.getLevel(bufferType, MediaType.Video) + + return@AsyncFunction mapOf( + "audio" to audioLevel.toJson(), + "video" to videoLevel.toJson() + ) } - } - /** - * Sets the target buffer level for the chosen buffer type across all media types. - * @param nativeId Target player id. - * @param type The [type of buffer][toBufferType] to set the target level for. - * @param value The value to set. - */ - @ReactMethod - fun setTargetLevel(nativeId: NativeId, type: String, value: Double, promise: Promise) { - promise.unit.resolveOnUiThread { - getPlayer(nativeId).buffer.setTargetLevel(type.toBufferTypeOrThrow(), value) + AsyncFunction("setTargetLevel") { playerId: String, type: String, value: Double -> + // Access PlayerModule to retrieve player + val player = appContext.registry.getModule()?.getPlayerOrNull(playerId) + ?: return@AsyncFunction + + player.buffer.setTargetLevel(type.toBufferTypeOrThrow(), value) } } - - private fun String.toBufferTypeOrThrow() = toBufferType() ?: throw IllegalArgumentException(INVALID_BUFFER_TYPE) } - -/** - * Representation of the React Native API `BufferLevels` object. - * This is necessary as we need a unified representation of the different APIs from both Android and iOS. - */ -data class RNBufferLevels(val audio: BufferLevel, val video: BufferLevel) diff --git a/android/src/main/java/com/bitmovin/player/reactnative/CustomMessageHandlerModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/CustomMessageHandlerModule.kt new file mode 100644 index 00000000..4a8ead00 --- /dev/null +++ b/android/src/main/java/com/bitmovin/player/reactnative/CustomMessageHandlerModule.kt @@ -0,0 +1,84 @@ +package com.bitmovin.player.reactnative + +import androidx.core.os.bundleOf +import com.bitmovin.player.reactnative.ui.CustomMessageHandlerBridge +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition + +/** + * Expo module for CustomMessageHandler management with bidirectional communication. + * Handles synchronous and asynchronous message handling between native code and JavaScript. + */ +class CustomMessageHandlerModule : Module() { + /** + * In-memory mapping from `nativeId`s to `CustomMessageHandlerBridge` instances. + */ + private val customMessageHandlers: Registry = mutableMapOf() + + /** + * ResultWaiter for synchronous message handling + */ + private val synchronousMessageWaiter = ResultWaiter() + + override fun definition() = ModuleDefinition { + Name("CustomMessageHandlerModule") + + OnDestroy { + customMessageHandlers.clear() + synchronousMessageWaiter.clear() + } + + Events("onReceivedSynchronousMessage", "onReceivedAsynchronousMessage") + + AsyncFunction("registerHandler") { nativeId: NativeId -> + val customMessageHandler = customMessageHandlers[nativeId] ?: CustomMessageHandlerBridge( + nativeId, + this@CustomMessageHandlerModule, + ) + customMessageHandlers[nativeId] = customMessageHandler + } + + AsyncFunction("destroy") { nativeId: NativeId -> + customMessageHandlers.remove(nativeId) + } + + AsyncFunction("onReceivedSynchronousMessageResult") { id: Int, result: String? -> + synchronousMessageWaiter.complete(id, result) + } + + AsyncFunction("sendMessage") { nativeId: NativeId, message: String, data: String? -> + customMessageHandlers[nativeId]?.sendMessage(message, data) + } + } + + fun getInstance(nativeId: NativeId?): CustomMessageHandlerBridge? = customMessageHandlers[nativeId] + + fun receivedSynchronousMessage(nativeId: NativeId, message: String, data: String?): String? { + val (id, wait) = synchronousMessageWaiter.make(5000) // 5 second timeout + + // Send event to TypeScript using Expo module event system + sendEvent( + "onReceivedSynchronousMessage", + bundleOf( + "nativeId" to nativeId, + "id" to id, + "message" to message, + "data" to data, + ), + ) + + return wait() + } + + fun receivedAsynchronousMessage(nativeId: NativeId, message: String, data: String?) { + // Send event to TypeScript using Expo module event system + sendEvent( + "onReceivedAsynchronousMessage", + bundleOf( + "nativeId" to nativeId, + "message" to message, + "data" to data, + ), + ) + } +} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/DebugModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/DebugModule.kt index 98cff642..1c58bb5d 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/DebugModule.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/DebugModule.kt @@ -1,22 +1,14 @@ package com.bitmovin.player.reactnative import com.bitmovin.player.api.DebugConfig -import com.facebook.react.bridge.* -import com.facebook.react.module.annotations.ReactModule +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition -private const val MODULE_NAME = "DebugModule" +class DebugModule : Module() { + override fun definition() = ModuleDefinition { + Name("DebugModule") -@ReactModule(name = MODULE_NAME) -class DebugModule(context: ReactApplicationContext) : BitmovinBaseModule(context) { - override fun getName() = MODULE_NAME - - /** - * Enable/disable verbose logging for the console logger. - * @param enabled Whether to set verbose logging as enabled or disabled. - */ - @ReactMethod - fun setDebugLoggingEnabled(enabled: Boolean, promise: Promise) { - promise.unit.resolveOnUiThread { + AsyncFunction("setDebugLoggingEnabled") { enabled: Boolean -> DebugConfig.isLoggingEnabled = enabled } } diff --git a/android/src/main/java/com/bitmovin/player/reactnative/DecoderConfigModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/DecoderConfigModule.kt new file mode 100644 index 00000000..ccafb2ed --- /dev/null +++ b/android/src/main/java/com/bitmovin/player/reactnative/DecoderConfigModule.kt @@ -0,0 +1,126 @@ +package com.bitmovin.player.reactnative + +import androidx.concurrent.futures.CallbackToFutureAdapter +import androidx.core.os.bundleOf +import com.bitmovin.player.api.decoder.DecoderConfig +import com.bitmovin.player.api.decoder.DecoderPriorityProvider +import com.bitmovin.player.api.decoder.MediaCodecInfo +import com.bitmovin.player.reactnative.converter.toJson +import com.bitmovin.player.reactnative.converter.toMediaCodecInfoList +import expo.modules.kotlin.exception.CodedException +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition +import java.util.concurrent.ConcurrentHashMap + +class DecoderConfigModule : Module() { + + /** + * In-memory mapping from `nativeId`s to `DecoderConfig` instances. + * This must match the Registry pattern from legacy DecoderConfigModule + */ + private val decoderConfigs: Registry = mutableMapOf() + private val overrideDecoderPriorityProviderCompleters = + ConcurrentHashMap>>() + + override fun definition() = ModuleDefinition { + Name("DecoderConfigModule") + + Events("onOverrideDecodersPriority") + + OnCreate { + // Module initialization + } + + OnDestroy { + decoderConfigs.clear() + overrideDecoderPriorityProviderCompleters.clear() + } + + /** + * Creates a new `DecoderConfig` instance inside the internal decoder configs using the provided `config` object. + */ + AsyncFunction("initializeWithConfig") { nativeId: NativeId, config: Map -> + if (decoderConfigs.containsKey(nativeId)) { + return@AsyncFunction + } + + val playbackConfig = config["playbackConfig"] as? Map + if (playbackConfig?.containsKey("decoderConfig") != true) { + return@AsyncFunction + } + + val decoderConfig = DecoderConfig( + decoderPriorityProvider = object : DecoderPriorityProvider { + override fun overrideDecodersPriority( + context: DecoderPriorityProvider.DecoderContext, + preferredDecoders: List, + ): List { + return overrideDecoderPriorityProvider(nativeId, context, preferredDecoders) + } + }, + ) + decoderConfigs[nativeId] = decoderConfig + } + + /** + * Completes the decoder priority provider override process + */ + AsyncFunction("overrideDecoderPriorityProviderComplete") { nativeId: NativeId, + response: List>, -> + val completer = overrideDecoderPriorityProviderCompleters[nativeId] + ?: throw DecoderConfigException.NoCompleterFound(nativeId) + + val mediaCodecInfoList = response.toMediaCodecInfoList() + completer.set(mediaCodecInfoList) + overrideDecoderPriorityProviderCompleters.remove(nativeId) + } + + /** + * Destroys the `DecoderConfig` instance referenced by `nativeId` + */ + AsyncFunction("destroy") { nativeId: NativeId -> + decoderConfigs.remove(nativeId) + // Remove all completers that start with this nativeId + overrideDecoderPriorityProviderCompleters.keys.filter { it.startsWith(nativeId) }.forEach { + overrideDecoderPriorityProviderCompleters.remove(it) + } + } + } + + /** + * Helper function to handle decoder priority provider override + */ + private fun overrideDecoderPriorityProvider( + nativeId: NativeId, + context: DecoderPriorityProvider.DecoderContext, + preferredDecoders: List, + ): List { + return CallbackToFutureAdapter.getFuture { completer -> + overrideDecoderPriorityProviderCompleters[nativeId] = completer + // Send event to TypeScript with decoder context and preferred decoders + sendEvent( + "onOverrideDecodersPriority", + bundleOf( + "nativeId" to nativeId, + "context" to context.toJson(), + "preferredDecoders" to preferredDecoders.map { it.toJson() }, + ), + ) + + "overrideDecoderPriorityProvider" + }.get() + } + + val decoderConfig: DecoderConfig? + get() = decoderConfigs.values.firstOrNull() + + fun getDecoderConfig(nativeId: NativeId): DecoderConfig? = decoderConfigs[nativeId] +} + +// MARK: - Exception Definitions + +sealed class DecoderConfigException(message: String) : CodedException(message) { + class NoCompleterFound(nativeId: NativeId) : DecoderConfigException( + "No completer found for decoder config: $nativeId", + ) +} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/DrmModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/DrmModule.kt index 650df817..d18f3a9c 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/DrmModule.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/DrmModule.kt @@ -1,136 +1,113 @@ package com.bitmovin.player.reactnative import android.util.Base64 +import androidx.core.os.bundleOf import com.bitmovin.player.api.drm.PrepareLicenseCallback import com.bitmovin.player.api.drm.PrepareMessageCallback import com.bitmovin.player.api.drm.WidevineConfig import com.bitmovin.player.reactnative.converter.toWidevineConfig -import com.facebook.react.bridge.* -import com.facebook.react.module.annotations.ReactModule +import expo.modules.kotlin.Promise +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition import java.security.InvalidParameterException -import java.util.concurrent.locks.Condition -import java.util.concurrent.locks.ReentrantLock -import kotlin.concurrent.withLock /** * Represents some operation that transforms data as bytes. */ typealias PrepareCallback = (ByteArray) -> ByteArray -private const val MODULE_NAME = "DrmModule" - -@ReactModule(name = MODULE_NAME) -class DrmModule(context: ReactApplicationContext) : BitmovinBaseModule(context) { +/** + * Expo module for DRM configuration management with Widevine DRM support. + * Handles bidirectional communication for DRM preparation callbacks. + */ +class DrmModule : Module() { /** * In-memory mapping from `nativeId`s to `WidevineConfig` instances. */ private val drmConfigs: Registry = mutableMapOf() /** - * Module's local lock object used to sync calls between Kotlin and JS. + * Shared ResultWaiter for all DRM callbacks */ - private val lock = ReentrantLock() + private val waiter = ResultWaiter() - /** - * Mapping between an object's `nativeId` and the value that'll be returned by its `prepareMessage` callback. - */ - private val preparedMessages: Registry = mutableMapOf() + override fun definition() = ModuleDefinition { + Name("DrmModule") - /** - * Lock condition used to sync read/write operations on `preparedMessages`. - */ - private val preparedMessagesCondition = lock.newCondition() + OnDestroy { + drmConfigs.clear() + waiter.clear() + } - /** - * Mapping between an object's `nativeId` and the value that'll be returned by its `prepareLicense` callback. - */ - private val preparedLicenses: Registry = mutableMapOf() + Events("onPrepareMessage", "onPrepareLicense") - /** - * Lock condition used to sync read/write operations on `preparedMessages`. - */ - private val preparedLicensesCondition = lock.newCondition() + AsyncFunction("initializeWithConfig") { nativeId: NativeId, config: Map, promise: Promise -> + if (drmConfigs.containsKey(nativeId)) { + promise.reject("DrmError", "NativeId already exists $nativeId", null) + return@AsyncFunction + } - /** - * JS exported module name. - */ - override fun getName() = MODULE_NAME + try { + val widevineConfig = config.toWidevineConfig() ?: throw InvalidParameterException( + "Invalid widevine config", + ) + widevineConfig.prepareMessageCallback = buildPrepareMessageCallback(nativeId, config) + widevineConfig.prepareLicenseCallback = buildPrepareLicense(nativeId, config) + drmConfigs[nativeId] = widevineConfig + promise.resolve(null) + } catch (e: Exception) { + promise.reject("DrmError", "Failed to initialize DRM config", e) + } + } - /** - * Fetches the `WidevineConfig` instance associated with `nativeId` from internal drmConfigs. - * @param nativeId `WidevineConfig` instance ID. - * @return The associated `WidevineConfig` instance or `null`. - */ - fun getConfig(nativeId: NativeId?): WidevineConfig? { - if (nativeId == null) { - return null + AsyncFunction("destroy") { nativeId: NativeId -> + drmConfigs.remove(nativeId) } - return drmConfigs[nativeId] - } - /** - * Creates a new `WidevineConfig` instance inside the internal drmConfigs using the provided `config` object. - * @param nativeId ID to associate with the `WidevineConfig` instance. - * @param config `DrmConfig` object received from JS. - */ - @ReactMethod - fun initWithConfig(nativeId: NativeId, config: ReadableMap, promise: Promise) { - promise.unit.resolveOnUiThread { - if (drmConfigs.containsKey(nativeId)) { - throw InvalidParameterException("NativeId already exists $nativeId") - } - val widevineConfig = config.toWidevineConfig() ?: throw InvalidParameterException("Invalid widevine config") - widevineConfig.prepareMessageCallback = buildPrepareMessageCallback(nativeId, config) - widevineConfig.prepareLicenseCallback = buildPrepareLicense(nativeId, config) - drmConfigs[nativeId] = widevineConfig + Function("setPreparedMessage") { id: Int, message: String -> + waiter.complete(id, message) } - } - /** - * Removes the `WidevineConfig` instance associated with `nativeId` from the internal drmConfigs. - * @param nativeId `WidevineConfig` to be disposed. - */ - @ReactMethod - fun destroy(nativeId: NativeId) { - drmConfigs.remove(nativeId) - } + Function("setPreparedLicense") { id: Int, license: String -> + waiter.complete(id, license) + } - /** - * Function called from JS to store the computed `prepareMessage` return value for `nativeId`. - */ - @ReactMethod(isBlockingSynchronousMethod = true) - fun setPreparedMessage(nativeId: NativeId, message: String) { - lock.withLock { - preparedMessages[nativeId] = message - preparedMessagesCondition.signal() + // iOS-specific methods that return null on Android for compatibility + AsyncFunction("setPreparedCertificate") { _: String, _: String -> // No-op on Android + } + AsyncFunction("setPreparedSyncMessage") { _: String, _: String -> // No-op on Android + } + AsyncFunction("setPreparedLicenseServerUrl") { _: String, _: String -> // No-op on Android + } + AsyncFunction("setPreparedContentId") { _: String, _: String -> // No-op on Android } } /** - * Function called from JS to store the computed `prepareLicense` return value for `nativeId`. + * Fetches the `WidevineConfig` instance associated with `nativeId` from internal drmConfigs. + * @param nativeId `WidevineConfig` instance ID. + * @return The associated `WidevineConfig` instance or `null`. */ - @ReactMethod(isBlockingSynchronousMethod = true) - fun setPreparedLicense(nativeId: NativeId, license: String) { - lock.withLock { - preparedLicenses[nativeId] = license - preparedLicensesCondition.signal() + fun getConfig(nativeId: NativeId?): WidevineConfig? { + if (nativeId == null) { + return null } + return drmConfigs[nativeId] } /** * Initialize the `prepareMessage` block in the [widevineConfig] - * @param widevineConfig Instance ID. + * @param nativeId Instance ID. * @param config `DrmConfig` config object sent from JS. */ - private fun buildPrepareMessageCallback(nativeId: NativeId, config: ReadableMap): PrepareMessageCallback? { - if (config.getMap("widevine")?.hasKey("prepareMessage") != true) { + private fun buildPrepareMessageCallback(nativeId: NativeId, config: Map): PrepareMessageCallback? { + if ((config["widevine"] as? Map<*, *>)?.containsKey("prepareMessage") != true) { return null } val prepareMessageCallback = createPrepareCallback( nativeId, "onPrepareMessage", - preparedMessages, - preparedMessagesCondition, + waiter, ) return PrepareMessageCallback(prepareMessageCallback) } @@ -140,15 +117,14 @@ class DrmModule(context: ReactApplicationContext) : BitmovinBaseModule(context) * @param nativeId Instance ID. * @param config `DrmConfig` config object sent from JS. */ - private fun buildPrepareLicense(nativeId: NativeId, config: ReadableMap): PrepareLicenseCallback? { - if (config.getMap("widevine")?.hasKey("prepareLicense") != true) { + private fun buildPrepareLicense(nativeId: NativeId, config: Map): PrepareLicenseCallback? { + if ((config["widevine"] as? Map<*, *>)?.containsKey("prepareLicense") != true) { return null } val prepareLicense = createPrepareCallback( nativeId, "onPrepareLicense", - preparedLicenses, - preparedLicensesCondition, + waiter, ) return PrepareLicenseCallback(prepareLicense) } @@ -157,22 +133,27 @@ class DrmModule(context: ReactApplicationContext) : BitmovinBaseModule(context) * Creates the body of a preparation callback e.g. `prepareMessage`, `prepareLicense`, etc. * @param nativeId Instance ID. * @param method JS prepare callback name. - * @param registry Registry where JS preparation result will be stored. + * @param waiter ResultWaiter for handling async response. * @return The preparation callback function. */ private fun createPrepareCallback( nativeId: NativeId, method: String, - registry: Registry, - registryCondition: Condition, + waiter: ResultWaiter, ): PrepareCallback = { - val args = Arguments.createArray() - args.pushString(Base64.encodeToString(it, Base64.NO_WRAP)) - context.catalystInstance.callFunction("DRM-$nativeId", method, args as NativeArray) - lock.withLock { - registryCondition.await() - val result = registry[nativeId] - Base64.decode(result, Base64.NO_WRAP) - } + val (id, wait) = waiter.make(5000) // 5 second timeout + + // Send event to TypeScript using Expo module event system + sendEvent( + method, + bundleOf( + "nativeId" to nativeId, + "id" to id, + "data" to Base64.encodeToString(it, Base64.NO_WRAP), + ), + ) + + val result = wait() ?: "" + Base64.decode(result, Base64.NO_WRAP) } } diff --git a/android/src/main/java/com/bitmovin/player/reactnative/EventRelay.kt b/android/src/main/java/com/bitmovin/player/reactnative/EventRelay.kt deleted file mode 100644 index 2290bb3a..00000000 --- a/android/src/main/java/com/bitmovin/player/reactnative/EventRelay.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.bitmovin.player.reactnative - -import com.bitmovin.player.api.event.Event -import com.bitmovin.player.api.event.EventEmitter -import kotlin.reflect.KClass - -private data class Subscription( - val eventClass: KClass, - val action: (E) -> Unit, -) - -/** - * Attaches and detaches listener for the provided forwarding events on the current [EventEmitter] instance and - * relays the received events together with their associated name to the provided event output. - */ -class EventRelay, T : Event>( - /** - * List of events that should be relayed and their associated name. - */ - forwardingEventClassesAndNameMapping: Map, String>, - /** - * Is called for every relayed event together with its associated name. - */ - private val eventOutput: (String, Event) -> Unit, -) { - private val eventListeners = forwardingEventClassesAndNameMapping.map { - Subscription(it.key) { event -> eventOutput(it.value, event) } - } - - /** - * The [EventEmitter] for which the events are relayed. - */ - var eventEmitter: E? = null - set(value) { - field?.detachListeners(eventListeners) - field = value - value?.attachListeners(eventListeners) - } -} - -private fun EventEmitter.attachListeners(eventListener: List>) { - eventListener.forEach { on(it.eventClass, it.action) } -} - -private fun EventEmitter.detachListeners(eventListener: List>) { - eventListener.forEach { off(it.eventClass, it.action) } -} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/EventSubscription.kt b/android/src/main/java/com/bitmovin/player/reactnative/EventSubscription.kt new file mode 100644 index 00000000..a92a2711 --- /dev/null +++ b/android/src/main/java/com/bitmovin/player/reactnative/EventSubscription.kt @@ -0,0 +1,22 @@ +package com.bitmovin.player.reactnative + +import com.bitmovin.player.api.event.Event +import com.bitmovin.player.api.event.EventListener +import kotlin.reflect.KClass + +/** + * Data class representing an event subscription for Bitmovin Player events. + * This class encapsulates the event class type and the corresponding action to be executed + * when the event is triggered. + * + * @param eventClass The KClass of the event to subscribe to + * @param action The function to execute when the event is triggered + */ +data class EventSubscription ( + val eventClass: KClass, + val action: (E) -> Unit, +) : EventListener { + override fun onEvent(event: E) { + action(event) + } +} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/FullscreenHandlerModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/FullscreenHandlerModule.kt new file mode 100644 index 00000000..43468ce8 --- /dev/null +++ b/android/src/main/java/com/bitmovin/player/reactnative/FullscreenHandlerModule.kt @@ -0,0 +1,100 @@ +package com.bitmovin.player.reactnative + +import com.bitmovin.player.reactnative.ui.FullscreenHandlerBridge +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition + +/** + * Expo module for FullscreenHandler management with bidirectional communication. + * Handles synchronous fullscreen state changes between native code and JavaScript. + */ +class FullscreenHandlerModule : Module() { + /** + * In-memory mapping from `nativeId`s to `FullscreenHandlerBridge` instances. + */ + private val fullscreenHandlers: Registry = mutableMapOf() + + /** + * ResultWaiter used for blocking thread while waiting for fullscreen state change + */ + private val waiter = ResultWaiter() + + override fun definition() = ModuleDefinition { + Name("FullscreenHandlerModule") + + OnDestroy { + fullscreenHandlers.clear() + waiter.clear() + } + + Events("onEnterFullscreen", "onExitFullscreen") + + AsyncFunction("registerHandler") { nativeId: NativeId -> + if (fullscreenHandlers[nativeId] == null) { + fullscreenHandlers[nativeId] = FullscreenHandlerBridge(nativeId, this@FullscreenHandlerModule) + } + } + + AsyncFunction("destroy") { nativeId: NativeId -> + fullscreenHandlers.remove(nativeId) + } + + AsyncFunction("notifyFullscreenChanged") { id: Int, isFullscreenEnabled: Boolean -> + waiter.complete(id, isFullscreenEnabled) + } + + AsyncFunction("setIsFullscreenActive") { nativeId: NativeId, isFullscreenActive: Boolean -> + fullscreenHandlers[nativeId]?.isFullscreen = isFullscreenActive + } + } + + /** + * Retrieves the FullscreenHandlerBridge instance for the given nativeId. + * This method maintains the same static access pattern as the legacy module. + */ + fun getInstance(nativeId: NativeId?): FullscreenHandlerBridge? = fullscreenHandlers[nativeId] + + /** + * Handles fullscreen enter request from native code. + * Called by FullscreenHandlerBridge when fullscreen should be entered. + */ + fun requestEnterFullscreen(nativeId: NativeId) { + val handler = getInstance(nativeId) ?: return + + val (id, wait) = waiter.make(250) // 250ms timeout + + // Send event to JavaScript + sendEvent( + "onEnterFullscreen", + mapOf( + "nativeId" to nativeId, + "id" to id, + ), + ) + + val result = wait() ?: return + handler.isFullscreen = result + } + + /** + * Handles fullscreen exit request from native code. + * Called by FullscreenHandlerBridge when fullscreen should be exited. + */ + fun requestExitFullscreen(nativeId: NativeId) { + val handler = getInstance(nativeId) ?: return + + val (id, wait) = waiter.make(250) // 250ms timeout + + // Send event to JavaScript + sendEvent( + "onExitFullscreen", + mapOf( + "nativeId" to nativeId, + "id" to id, + ), + ) + + val result = wait() ?: return + handler.isFullscreen = result + } +} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/MediaSessionPlaybackManager.kt b/android/src/main/java/com/bitmovin/player/reactnative/MediaSessionPlaybackManager.kt index 89833dab..96a863b1 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/MediaSessionPlaybackManager.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/MediaSessionPlaybackManager.kt @@ -6,11 +6,10 @@ import android.content.Intent import android.content.ServiceConnection import android.os.IBinder import com.bitmovin.player.api.Player -import com.bitmovin.player.reactnative.extensions.playerModule import com.bitmovin.player.reactnative.services.MediaSessionPlaybackService -import com.facebook.react.bridge.* +import expo.modules.kotlin.AppContext -class MediaSessionPlaybackManager(val context: ReactApplicationContext) { +class MediaSessionPlaybackManager(val appContext: AppContext) { private var serviceBinder: MediaSessionPlaybackService.ServiceBinder? = null private var playerId: NativeId? = null val player: Player? @@ -33,6 +32,8 @@ class MediaSessionPlaybackManager(val context: ReactApplicationContext) { fun setupMediaSessionPlayback(playerId: NativeId) { this.playerId = playerId + val context = appContext.reactContext + ?: throw IllegalStateException("React context is not available") val intent = Intent(context, MediaSessionPlaybackService::class.java) intent.action = Intent.ACTION_MEDIA_BUTTON val connection: ServiceConnection = MediaSessionPlaybackServiceConnection() @@ -49,7 +50,6 @@ class MediaSessionPlaybackManager(val context: ReactApplicationContext) { private fun getPlayer( nativeId: NativeId? = playerId, - playerModule: PlayerModule? = context.playerModule, - ): Player = nativeId?.let { playerModule?.getPlayerOrNull(nativeId) } + ): Player = playerId?.let { appContext.registry.getModule()?.getPlayerOrNull(it) } ?: throw IllegalArgumentException("Invalid PlayerId $nativeId") } diff --git a/android/src/main/java/com/bitmovin/player/reactnative/NetworkModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/NetworkModule.kt index 4cbc854c..15cb333c 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/NetworkModule.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/NetworkModule.kt @@ -3,6 +3,7 @@ package com.bitmovin.player.reactnative import android.util.Log import androidx.concurrent.futures.CallbackToFutureAdapter import androidx.concurrent.futures.CallbackToFutureAdapter.Completer +import androidx.core.os.bundleOf import com.bitmovin.player.api.network.HttpRequest import com.bitmovin.player.api.network.HttpRequestType import com.bitmovin.player.api.network.HttpResponse @@ -13,16 +14,17 @@ import com.bitmovin.player.reactnative.converter.toHttpRequest import com.bitmovin.player.reactnative.converter.toHttpResponse import com.bitmovin.player.reactnative.converter.toJson import com.bitmovin.player.reactnative.converter.toNetworkConfig -import com.facebook.react.bridge.* -import com.facebook.react.module.annotations.ReactModule +import expo.modules.kotlin.Promise +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.Future -private const val MODULE_NAME = "NetworkModule" - -@ReactModule(name = MODULE_NAME) -class NetworkModule(context: ReactApplicationContext) : BitmovinBaseModule(context) { - +/** + * Expo module for NetworkConfig management with HTTP request/response preprocessing. + * Handles bidirectional communication between native code and JavaScript for network operations. + */ +class NetworkModule : Module() { /** * In-memory mapping from `nativeId`s to `NetworkConfig` instances. */ @@ -30,49 +32,84 @@ class NetworkModule(context: ReactApplicationContext) : BitmovinBaseModule(conte private val preprocessHttpRequestCompleters = ConcurrentHashMap>() private val preprocessHttpResponseCompleters = ConcurrentHashMap>() - override fun getName() = MODULE_NAME + override fun definition() = ModuleDefinition { + Name("NetworkModule") - fun getConfig(nativeId: NativeId?): NetworkConfig? = nativeId?.let { networkConfigs[it] } + OnDestroy { + networkConfigs.clear() + preprocessHttpRequestCompleters.clear() + preprocessHttpResponseCompleters.clear() + } - @ReactMethod - fun initWithConfig(nativeId: NativeId, config: ReadableMap, promise: Promise) { - promise.unit.resolveOnUiThread { + Events("onPreprocessHttpRequest", "onPreprocessHttpResponse") + + AsyncFunction("initializeWithConfig") { nativeId: NativeId, config: Map, promise: Promise -> if (networkConfigs.containsKey(nativeId)) { - return@resolveOnUiThread + promise.resolve(null) + return@AsyncFunction + } + + try { + val networkConfig = config.toNetworkConfig() + networkConfigs[nativeId] = networkConfig + initConfigBlocks(nativeId, config) + promise.resolve(null) + } catch (e: Exception) { + promise.reject("NetworkError", "Failed to initialize network config", e) + } + } + + AsyncFunction("destroy") { nativeId: NativeId -> + networkConfigs.remove(nativeId) + + // Clean up completion handlers + preprocessHttpRequestCompleters.keys.filter { it.startsWith(nativeId) }.forEach { + preprocessHttpRequestCompleters.remove(it) + } + preprocessHttpResponseCompleters.keys.filter { it.startsWith(nativeId) }.forEach { + preprocessHttpResponseCompleters.remove(it) } - val networkConfig = config.toNetworkConfig() - networkConfigs[nativeId] = networkConfig - initConfigBlocks(nativeId, config) } - } - @ReactMethod - fun destroy(nativeId: NativeId) { - networkConfigs.remove(nativeId) - preprocessHttpRequestCompleters.keys.filter { it.startsWith(nativeId) }.forEach { - preprocessHttpRequestCompleters.remove(it) + AsyncFunction("setPreprocessedHttpRequest") { requestId: String, request: Map -> + val completer = preprocessHttpRequestCompleters.remove(requestId) + if (completer == null) { + Log.e("NetworkModule", "Completer is null for requestId: $requestId, this can cause stuck network requests") + return@AsyncFunction + } + completer.set(request.toHttpRequest()) } - preprocessHttpResponseCompleters.keys.filter { it.startsWith(nativeId) }.forEach { - preprocessHttpResponseCompleters.remove(it) + + AsyncFunction("setPreprocessedHttpResponse") { responseId: String, response: Map -> + preprocessHttpResponseCompleters[responseId]?.set(response.toHttpResponse()) + preprocessHttpResponseCompleters.remove(responseId) } } - private fun initConfigBlocks(nativeId: String, config: ReadableMap) { - initPreprocessHttpRequest(nativeId, networkConfigJson = config) - initPreprocessHttpResponse(nativeId, networkConfigJson = config) + /** + * Retrieves the NetworkConfig instance for the given nativeId. + * This method maintains the same static access pattern as the legacy module. + */ + fun getConfig(nativeId: NativeId?): NetworkConfig? = nativeId?.let { networkConfigs[it] } + + private fun initConfigBlocks(nativeId: NativeId, config: Map) { + initPreprocessHttpRequest(nativeId, config) + initPreprocessHttpResponse(nativeId, config) } - private fun initPreprocessHttpRequest(nativeId: NativeId, networkConfigJson: ReadableMap) { + private fun initPreprocessHttpRequest(nativeId: NativeId, networkConfigJson: Map) { val networkConfig = getConfig(nativeId) ?: return - if (!networkConfigJson.hasKey("preprocessHttpRequest")) return + if (!networkConfigJson.containsKey("preprocessHttpRequest")) return + networkConfig.preprocessHttpRequestCallback = PreprocessHttpRequestCallback { type, request -> preprocessHttpRequestFromJS(nativeId, type, request) } } - private fun initPreprocessHttpResponse(nativeId: NativeId, networkConfigJson: ReadableMap) { + private fun initPreprocessHttpResponse(nativeId: NativeId, networkConfigJson: Map) { val networkConfig = getConfig(nativeId) ?: return - if (!networkConfigJson.hasKey("preprocessHttpResponse")) return + if (!networkConfigJson.containsKey("preprocessHttpResponse")) return + networkConfig.preprocessHttpResponseCallback = PreprocessHttpResponseCallback { type, response -> preprocessHttpResponseFromJS(nativeId, type, response) } @@ -84,25 +121,28 @@ class NetworkModule(context: ReactApplicationContext) : BitmovinBaseModule(conte request: HttpRequest, ): Future { val requestId = "$nativeId@${System.identityHashCode(request)}" - val args = Arguments.createArray() - args.pushString(requestId) - args.pushString(type.toJson()) - args.pushMap(request.toJson()) + val args = mapOf( + "requestId" to requestId, + "type" to type.toJson(), + "request" to request.toJson(), + ) return CallbackToFutureAdapter.getFuture { completer -> preprocessHttpRequestCompleters[requestId] = completer - context.catalystInstance.callFunction("Network-$nativeId", "onPreprocessHttpRequest", args as NativeArray) - } - } - @ReactMethod - fun setPreprocessedHttpRequest(requestId: String, request: ReadableMap) { - val completer = preprocessHttpRequestCompleters.remove(requestId) - if (completer == null) { - Log.e(MODULE_NAME, "Completer is null for requestId: $requestId, this can cause stuck network requests") - return + // Send event to TypeScript using Expo module event system + sendEvent( + "onPreprocessHttpRequest", + bundleOf( + "nativeId" to nativeId, + "requestId" to requestId, + "type" to type.toJson(), + "request" to request.toJson(), + ), + ) + + return@getFuture "NetworkModule-preprocessHttpRequest-$requestId" } - completer.set(request.toHttpRequest()) } private fun preprocessHttpResponseFromJS( @@ -111,20 +151,22 @@ class NetworkModule(context: ReactApplicationContext) : BitmovinBaseModule(conte response: HttpResponse, ): Future { val responseId = "$nativeId@${System.identityHashCode(response)}" - val args = Arguments.createArray() - args.pushString(responseId) - args.pushString(type.toJson()) - args.pushMap(response.toJson()) return CallbackToFutureAdapter.getFuture { completer -> preprocessHttpResponseCompleters[responseId] = completer - context.catalystInstance.callFunction("Network-$nativeId", "onPreprocessHttpResponse", args as NativeArray) - } - } - @ReactMethod - fun setPreprocessedHttpResponse(responseId: String, response: ReadableMap) { - preprocessHttpResponseCompleters[responseId]?.set(response.toHttpResponse()) - preprocessHttpResponseCompleters.remove(responseId) + // Send event to TypeScript using Expo module event system + sendEvent( + "onPreprocessHttpResponse", + bundleOf( + "nativeId" to nativeId, + "responseId" to responseId, + "type" to type.toJson(), + "response" to response.toJson(), + ), + ) + + return@getFuture "NetworkModule-preprocessHttpResponse-$responseId" + } } } diff --git a/android/src/main/java/com/bitmovin/player/reactnative/OfflineModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/OfflineModule.kt index ea5d3220..a6beebf6 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/OfflineModule.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/OfflineModule.kt @@ -2,259 +2,200 @@ package com.bitmovin.player.reactnative import com.bitmovin.player.api.offline.options.OfflineOptionEntryState import com.bitmovin.player.reactnative.converter.toSourceConfig -import com.bitmovin.player.reactnative.extensions.drmModule -import com.bitmovin.player.reactnative.extensions.getIntOrNull -import com.bitmovin.player.reactnative.extensions.getStringArray import com.bitmovin.player.reactnative.offline.OfflineContentManagerBridge import com.bitmovin.player.reactnative.offline.OfflineDownloadRequest -import com.facebook.react.bridge.* -import com.facebook.react.module.annotations.ReactModule +import expo.modules.kotlin.exception.CodedException +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition import java.security.InvalidParameterException -private const val OFFLINE_MODULE = "BitmovinOfflineModule" - -@ReactModule(name = OFFLINE_MODULE) -class OfflineModule(context: ReactApplicationContext) : BitmovinBaseModule(context) { +class OfflineModule : Module() { /** - * In-memory mapping from `nativeId`s to `OfflineManager` instances. + * In-memory mapping from `nativeId`s to `OfflineContentManagerBridge` instances. + * This must match the Registry pattern from legacy OfflineModule */ private val offlineContentManagerBridges: Registry = mutableMapOf() - /** - * JS exported module name. - */ - override fun getName() = OFFLINE_MODULE + override fun definition() = ModuleDefinition { + Name("OfflineModule") - /** - * Fetches the `OfflineManager` instance associated with `nativeId` from the internal offline managers. - */ - fun getOfflineContentManagerBridgeOrNull( - nativeId: NativeId, - ): OfflineContentManagerBridge? = offlineContentManagerBridges[nativeId] - - private fun RejectPromiseOnExceptionBlock.getOfflineContentManagerBridge( - nativeId: NativeId, - ): OfflineContentManagerBridge = offlineContentManagerBridges[nativeId] - ?: throw IllegalArgumentException("No offline content manager bridge for id $nativeId") - - override fun invalidate() { - super.invalidate() - context.runOnUiQueueThread { - offlineContentManagerBridges.keys.forEach { nativeId -> - getOfflineContentManagerBridgeOrNull(nativeId)?.let { offlineContentManagerBridge -> - offlineContentManagerBridge.release() - offlineContentManagerBridges.remove(nativeId) + Events("onBitmovinOfflineEvent") + + OnCreate { + // Module initialization + } + + OnDestroy { + // Clean up offline content managers + offlineContentManagerBridges.values.toList().forEach { bridge -> + try { + bridge.release() + } catch (e: Exception) { + // Log but don't crash on cleanup } } + offlineContentManagerBridges.clear() } - } - - /** - * Callback when a new NativeEventEmitter is created from the Typescript layer. - */ - @ReactMethod - fun addListener(eventName: String?) { - // NO-OP - } - /** - * Callback when a NativeEventEmitter is removed from the Typescript layer. - */ - @ReactMethod - fun removeListeners(count: Int?) { - // NO-OP - } - - /** - * Creates a new `OfflineManager` instance inside the internal offline managers using the provided `config` object. - * @param config `ReadableMap` object received from JS. Should contain a sourceConfig and location. - */ - @ReactMethod - fun initWithConfig(nativeId: NativeId, config: ReadableMap?, drmNativeId: NativeId?, promise: Promise) { - promise.unit.resolveOnUiThread { + AsyncFunction("initializeWithConfig") { nativeId: NativeId, config: Map?, drmNativeId: NativeId? -> if (offlineContentManagerBridges.containsKey(nativeId)) { - throw InvalidParameterException("content manager bridge id already exists: $nativeId") + throw OfflineException.ManagerAlreadyExists(nativeId) } - val identifier = config?.getString("identifier") - ?.takeIf { it.isNotEmpty() } ?: throw IllegalArgumentException("invalid identifier") - val sourceConfig = config.getMap("sourceConfig")?.toSourceConfig() - ?: throw IllegalArgumentException("Invalid source config") + val identifier = config?.get("identifier") as? String + ?: throw OfflineException.InvalidIdentifier() + + val sourceConfig = (config["sourceConfig"] as? Map)?.toSourceConfig() + ?: throw OfflineException.InvalidSourceConfig() - sourceConfig.drmConfig = context.drmModule?.getConfig(drmNativeId) + // Get DRM config from DrmModule if available + sourceConfig.drmConfig = appContext.registry.getModule()?.getConfig(drmNativeId) + + val context = appContext.reactContext + ?: throw InvalidParameterException("ReactApplicationContext is not available") offlineContentManagerBridges[nativeId] = OfflineContentManagerBridge( nativeId, context, + this@OfflineModule, identifier, sourceConfig, - context.cacheDir.path, + appContext.cacheDirectory.path, ) } - } - @ReactMethod - fun getState(nativeId: NativeId, promise: Promise) { - promise.string.resolveWithBridge(nativeId) { - state.name + /** + * Gets the current state of the `OfflineContentManager` + */ + AsyncFunction("getState") { nativeId: NativeId -> + getOfflineContentManagerBridge(nativeId).state.name } - } - /** - * Starts the `OfflineContentManager`'s asynchronous process of fetching the `OfflineContentOptions`. - * When the options are loaded a device event will be fired where the event type is `BitmovinOfflineEvent` and the data has an event type of `onOptionsAvailable`. - * @param nativeId Target offline manager. - */ - @ReactMethod - fun getOptions(nativeId: NativeId, promise: Promise) { - promise.unit.resolveWithBridge(nativeId) { - getOptions() + /** + * Starts the `OfflineContentManager`'s asynchronous process of fetching the `OfflineContentOptions`. + * When the options are loaded a device event will be fired where the event type is `BitmovinOfflineEvent` * and the data has an event type of `onOptionsAvailable`. + */ + AsyncFunction("getOptions") { nativeId: NativeId -> + getOfflineContentManagerBridge(nativeId).getOptions() } - } - /** - * Enqueues downloads according to the `OfflineDownloadRequest`. - * The promise will reject in the event of null or invalid request parameters. - * The promise will reject an `IllegalOperationException` when selecting an `OfflineOptionEntry` to download that is not compatible with the current state. - * @param nativeId Target offline manager. - * @param request `ReadableMap` that contains the `OfflineManager.OfflineOptionType`, id, and `OfflineOptionEntryAction` necessary to set the new action. - */ - @ReactMethod - fun download(nativeId: NativeId, request: ReadableMap, promise: Promise) { - promise.unit.resolveWithBridge(nativeId) { - when (state) { - OfflineOptionEntryState.Downloaded -> throw IllegalStateException("Download already completed") - OfflineOptionEntryState.Downloading, OfflineOptionEntryState.Failed -> throw IllegalStateException( - "Download already in progress", - ) - OfflineOptionEntryState.Suspended -> throw IllegalStateException("Download is suspended") + /** + * Enqueues downloads according to the `OfflineDownloadRequest`. + * The promise will reject in the event of null or invalid request parameters. + */ + AsyncFunction("download") { nativeId: NativeId, request: Map -> + val bridge = getOfflineContentManagerBridge(nativeId) + + when (bridge.state) { + OfflineOptionEntryState.Downloaded -> throw OfflineException.DownloadAlreadyCompleted() + OfflineOptionEntryState.Downloading, OfflineOptionEntryState.Failed -> + throw OfflineException.DownloadInProgress() + OfflineOptionEntryState.Suspended -> throw OfflineException.DownloadSuspended() else -> {} } - val minimumBitRate = request.getIntOrNull("minimumBitrate")?.also { - if (it < 0) throw IllegalArgumentException("Invalid download request") + + val minimumBitRate = request["minimumBitrate"] as? Int + if (minimumBitRate != null && minimumBitRate < 0) { + throw OfflineException.InvalidRequest() } - val audioOptionIds = request.getStringArray("audioOptionIds")?.filterNotNull() - val textOptionIds = request.getStringArray("textOptionIds")?.filterNotNull() - process(OfflineDownloadRequest(minimumBitRate, audioOptionIds, textOptionIds)) + val audioOptionIds = (request["audioOptionIds"] as? List<*>)?.filterIsInstance() + val textOptionIds = (request["textOptionIds"] as? List<*>)?.filterIsInstance() + + bridge.process(OfflineDownloadRequest(minimumBitRate, audioOptionIds, textOptionIds)) } - } - /** - * Resumes all suspended actions. - * @param nativeId Target offline manager. - */ - @ReactMethod - fun resume(nativeId: NativeId, promise: Promise) { - promise.unit.resolveWithBridge(nativeId) { - resume() + /** + * Resumes all suspended actions. + */ + AsyncFunction("resume") { nativeId: NativeId -> + getOfflineContentManagerBridge(nativeId).resume() } - } - /** - * Suspends all active actions. - * @param nativeId Target offline manager. - */ - @ReactMethod - fun suspend(nativeId: NativeId, promise: Promise) { - promise.unit.resolveWithBridge(nativeId) { - suspend() + /** + * Suspends all active actions. + */ + AsyncFunction("suspend") { nativeId: NativeId -> + getOfflineContentManagerBridge(nativeId).suspend() } - } - /** - * Cancels and deletes the current download. - * @param nativeId Target offline manager. - */ - @ReactMethod - fun cancelDownload(nativeId: NativeId, promise: Promise) { - promise.unit.resolveWithBridge(nativeId) { - cancelDownload() + /** + * Cancels and deletes the current download. + */ + AsyncFunction("cancelDownload") { nativeId: NativeId -> + getOfflineContentManagerBridge(nativeId).cancelDownload() } - } - /** - * Resolve `nativeId`'s current `usedStorage`. - * @param nativeId Target offline manager. - */ - @ReactMethod - fun usedStorage(nativeId: NativeId, promise: Promise) { - promise.double.resolveWithBridge(nativeId) { - offlineContentManager.usedStorage.toDouble() + /** + * Resolve `nativeId`'s current `usedStorage`. + */ + AsyncFunction("usedStorage") { nativeId: NativeId -> + getOfflineContentManagerBridge(nativeId).offlineContentManager.usedStorage.toDouble() } - } - /** - * Deletes everything related to the related content ID. - * @param nativeId Target offline manager. - */ - @ReactMethod - fun deleteAll(nativeId: NativeId, promise: Promise) { - promise.unit.resolveWithBridge(nativeId) { - deleteAll() + /** + * Deletes everything related to the related content ID. + */ + AsyncFunction("deleteAll") { nativeId: NativeId -> + getOfflineContentManagerBridge(nativeId).deleteAll() } - } - /** - * Downloads the offline license. - * When finished successfully a device event will be fired where the event type is `BitmovinOfflineEvent` and the data has an event type of `onDrmLicenseUpdated`. - * Errors are transmitted by a device event will be fired where the event type is `BitmovinOfflineEvent` and the data has an event type of `onError`. - * @param nativeId Target offline manager. - */ - @ReactMethod - fun downloadLicense(nativeId: NativeId, promise: Promise) { - promise.unit.resolveWithBridge(nativeId) { - downloadLicense() + /** + * Downloads the offline license. + * When finished successfully a device event will be fired where the event type is `BitmovinOfflineEvent` * and the data has an event type of `onDrmLicenseUpdated`. + */ + AsyncFunction("downloadLicense") { nativeId: NativeId -> + getOfflineContentManagerBridge(nativeId).downloadLicense() } - } - /** - * Releases the currently held offline license. - * When finished successfully a device event will be fired where the event type is `BitmovinOfflineEvent` and the data has an event type of `onDrmLicenseUpdated`. - * Errors are transmitted by a device event will be fired where the event type is `BitmovinOfflineEvent` and the data has an event type of `onError`. - * @param nativeId Target offline manager. - */ - @ReactMethod - fun releaseLicense(nativeId: NativeId, promise: Promise) { - promise.unit.resolveWithBridge(nativeId) { - releaseLicense() + /** + * Releases the currently held offline license. + * When finished successfully a device event will be fired where the event type is `BitmovinOfflineEvent` * and the data has an event type of `onDrmLicenseUpdated`. + */ + AsyncFunction("releaseLicense") { nativeId: NativeId -> + getOfflineContentManagerBridge(nativeId).releaseLicense() } - } - /** - * Renews the already downloaded DRM license. - * When finished successfully a device event will be fired where the event type is `BitmovinOfflineEvent` and the data has an event type of `onDrmLicenseUpdated`. - * Errors are transmitted by a device event will be fired where the event type is `BitmovinOfflineEvent` and the data has an event type of `onError`. - * @param nativeId Target offline manager. - */ - @ReactMethod - fun renewOfflineLicense(nativeId: NativeId, promise: Promise) { - promise.unit.resolveWithBridge(nativeId) { - renewOfflineLicense() + /** + * Renews the already downloaded DRM license. + * When finished successfully a device event will be fired where the event type is `BitmovinOfflineEvent` * and the data has an event type of `onDrmLicenseUpdated`. + */ + AsyncFunction("renewOfflineLicense") { nativeId: NativeId -> + getOfflineContentManagerBridge(nativeId).renewOfflineLicense() } - } - /** - * Call `.release()` on `nativeId`'s offline manager. - * IMPORTANT: Call this when the component, in which it was created, is destroyed. - * The `OfflineManager` should not be used after calling this method. - * @param nativeId Target player Id. - */ - @ReactMethod - fun release(nativeId: NativeId, promise: Promise) { - promise.unit.resolveWithBridge(nativeId) { - release() + /** + * Call `.release()` on `nativeId`'s offline manager. + * IMPORTANT: Call this when the component, in which it was created, is destroyed. + * The `OfflineManager` should not be used after calling this method. + */ + AsyncFunction("release") { nativeId: NativeId -> + val bridge = getOfflineContentManagerBridge(nativeId) + bridge.release() offlineContentManagerBridges.remove(nativeId) } } - private inline fun TPromise.resolveWithBridge( - nativeId: NativeId, - crossinline block: OfflineContentManagerBridge.() -> T, - ) { - resolveOnCurrentThread { - getOfflineContentManagerBridge(nativeId).block() - } + /** + * Helper function to get OfflineContentManagerBridge with proper error handling + */ + fun getOfflineContentManagerBridge(nativeId: NativeId): OfflineContentManagerBridge { + return offlineContentManagerBridges[nativeId] ?: throw OfflineException.ManagerNotFound(nativeId) } } + +// MARK: - Exception Definitions + +sealed class OfflineException(message: String) : CodedException(message) { + class ManagerAlreadyExists(nativeId: NativeId) : OfflineException( + "Content manager bridge id already exists: $nativeId", + ) + class ManagerNotFound(nativeId: NativeId) : OfflineException("No offline content manager bridge for id $nativeId") + class InvalidIdentifier : OfflineException("Invalid identifier") + class InvalidSourceConfig : OfflineException("Invalid source config") + class InvalidRequest : OfflineException("Invalid download request") + class DownloadAlreadyCompleted : OfflineException("Download already completed") + class DownloadInProgress : OfflineException("Download already in progress") + class DownloadSuspended : OfflineException("Download is suspended") +} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/PlayerAnalyticsModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/PlayerAnalyticsModule.kt index 5d6de800..cb648df4 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/PlayerAnalyticsModule.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/PlayerAnalyticsModule.kt @@ -3,48 +3,49 @@ package com.bitmovin.player.reactnative import com.bitmovin.player.api.analytics.AnalyticsApi import com.bitmovin.player.api.analytics.AnalyticsApi.Companion.analytics import com.bitmovin.player.reactnative.converter.toAnalyticsCustomData -import com.facebook.react.bridge.* -import com.facebook.react.module.annotations.ReactModule +import expo.modules.kotlin.Promise +import expo.modules.kotlin.functions.Queues +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition -private const val MODULE_NAME = "PlayerAnalyticsModule" +/** + * Expo module for PlayerAnalytics management. + * Provides analytics functionality for player instances. + */ +class PlayerAnalyticsModule : Module() { -@ReactModule(name = MODULE_NAME) -class PlayerAnalyticsModule(context: ReactApplicationContext) : BitmovinBaseModule(context) { - /** - * JS exported module name. - */ - override fun getName() = MODULE_NAME + override fun definition() = ModuleDefinition { + Name("PlayerAnalyticsModule") - /** - * Sends a sample with the provided custom data. - * Does not change the configured custom data of the collector or source. - * @param playerId Native Id of the player instance. - * @param json Custom data config json. - */ - @ReactMethod - fun sendCustomDataEvent(playerId: NativeId, json: ReadableMap, promise: Promise) { - promise.unit.resolveOnUiThreadWithAnalytics(playerId) { - sendCustomDataEvent(json.toAnalyticsCustomData()) - } + AsyncFunction("sendCustomDataEvent") { playerId: String, json: Map, promise: Promise -> + try { + val analytics = getAnalyticsForPlayer(playerId) + analytics.sendCustomDataEvent(json.toAnalyticsCustomData()) + promise.resolve(null) + } catch (e: Exception) { + promise.reject("PlayerAnalyticsError", "Failed to send custom data event", e) + } + }.runOnQueue(Queues.MAIN) + + AsyncFunction("getUserId") { playerId: String, promise: Promise -> + try { + val analytics = getAnalyticsForPlayer(playerId) + promise.resolve(analytics.userId) + } catch (e: Exception) { + promise.reject("PlayerAnalyticsError", "Failed to get user ID", e) + } + }.runOnQueue(Queues.MAIN) } /** - * Gets the current user Id for a player instance with analytics. - * @param playerId Native Id of the the player instance. - * @param promise JS promise object. + * Helper method to get analytics for a player instance. */ - @ReactMethod - fun getUserId(playerId: NativeId, promise: Promise) { - promise.string.resolveOnUiThreadWithAnalytics(playerId) { - userId - } - } - - private inline fun TPromise.resolveOnUiThreadWithAnalytics( - playerId: NativeId, - crossinline block: AnalyticsApi.() -> T, - ) = resolveOnUiThread { - val analytics = getPlayer(playerId).analytics ?: throw IllegalStateException("Analytics is disabled") - analytics.block() + private fun getAnalyticsForPlayer(playerId: String): AnalyticsApi { + // Get the player from PlayerModule + val playerModule = appContext.registry.getModule() + val player = playerModule?.getPlayerOrNull(playerId) ?: throw IllegalStateException( + "Could not find player with ID $playerId", + ) + return player.analytics ?: throw IllegalStateException("Analytics is disabled") } } diff --git a/android/src/main/java/com/bitmovin/player/reactnative/PlayerModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/PlayerModule.kt index 0b6c8a4b..927f4918 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/PlayerModule.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/PlayerModule.kt @@ -1,613 +1,347 @@ package com.bitmovin.player.reactnative -import android.util.Log import com.bitmovin.analytics.api.DefaultMetadata import com.bitmovin.player.api.Player import com.bitmovin.player.api.PlayerConfig import com.bitmovin.player.api.analytics.create -import com.bitmovin.player.api.event.PlayerEvent import com.bitmovin.player.reactnative.converter.toAdItem import com.bitmovin.player.reactnative.converter.toAnalyticsConfig import com.bitmovin.player.reactnative.converter.toAnalyticsDefaultMetadata import com.bitmovin.player.reactnative.converter.toJson import com.bitmovin.player.reactnative.converter.toMediaControlConfig import com.bitmovin.player.reactnative.converter.toPlayerConfig -import com.bitmovin.player.reactnative.extensions.mapToReactArray -import com.facebook.react.bridge.* -import com.facebook.react.module.annotations.ReactModule -import java.security.InvalidParameterException - -private const val MODULE_NAME = "PlayerModule" - -@ReactModule(name = MODULE_NAME) -class PlayerModule(context: ReactApplicationContext) : BitmovinBaseModule(context) { - /** - * In-memory mapping from [NativeId]s to [Player] instances. - */ - private val players: Registry = mutableMapOf() - - val mediaSessionPlaybackManager = MediaSessionPlaybackManager(context) - - /** - * JS exported module name. - */ - override fun getName() = MODULE_NAME - - /** - * Fetches the `Player` instance associated with [nativeId] from the internal players. - */ - fun getPlayerOrNull(nativeId: NativeId): Player? = players[nativeId] - - override fun invalidate() { - super.invalidate() - context.runOnUiQueueThread { - players.keys.forEach { nativeId -> - getPlayerOrNull(nativeId)?.let { player -> - player.destroy() - players.remove(nativeId) - } - } - } - } +import com.bitmovin.player.reactnative.extensions.getMap +import expo.modules.kotlin.functions.Queues +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition - /** - * Creates a new `Player` instance inside the internal players using the provided `config` object. - * @param config `PlayerConfig` object received from JS. - */ - @ReactMethod - fun initWithConfig(nativeId: NativeId, config: ReadableMap?, networkNativeId: NativeId?, promise: Promise) { - init(nativeId, config, networkNativeId = networkNativeId, analyticsConfigJson = null, promise) - } +class PlayerModule : Module() { - /** - * Creates a new `Player` instance inside the internal players using the provided `playerConfig` and `analyticsConfig`. - * @param playerConfigJson `PlayerConfig` object received from JS. - * @param analyticsConfigJson `AnalyticsConfig` object received from JS. - */ - @ReactMethod - fun initWithAnalyticsConfig( - nativeId: NativeId, - playerConfigJson: ReadableMap?, - networkNativeId: NativeId?, - analyticsConfigJson: ReadableMap, - promise: Promise, - ) = init(nativeId, playerConfigJson, networkNativeId, analyticsConfigJson, promise) + val mediaSessionPlaybackManager by lazy { MediaSessionPlaybackManager(appContext) } - private fun init( - nativeId: NativeId, - playerConfigJson: ReadableMap?, - networkNativeId: NativeId?, - analyticsConfigJson: ReadableMap?, - promise: Promise, - ) = promise.unit.resolveOnUiThread { - if (players.containsKey(nativeId)) { - if (playerConfigJson != null || analyticsConfigJson != null) { - Log.w("BitmovinPlayerModule", "Cannot reconfigure an existing player") - } - return@resolveOnUiThread // key can be reused to access the same native instance (see NativeInstanceConfig) - } - val playerConfig = playerConfigJson?.toPlayerConfig() ?: PlayerConfig() - val analyticsConfig = analyticsConfigJson?.toAnalyticsConfig() - val defaultMetadata = analyticsConfigJson?.getMap("defaultMetadata")?.toAnalyticsDefaultMetadata() - val enableMediaSession = playerConfigJson?.getMap("mediaControlConfig") - ?.toMediaControlConfig()?.isEnabled ?: true + override fun definition() = ModuleDefinition { + Name("PlayerModule") - val networkConfig = networkNativeId?.let { networkModule.getConfig(it) } - if (networkConfig != null) { - playerConfig.networkConfig = networkConfig + OnCreate { + // Module initialization } - players[nativeId] = if (analyticsConfig == null) { - Player.create(context, playerConfig) - } else { - Player.create( - context = context, - playerConfig = playerConfig, - analyticsConfig = analyticsConfig, - defaultMetadata = defaultMetadata ?: DefaultMetadata(), - ) - } + OnDestroy { + // Clean up all players when module is destroyed + PlayerRegistry.getAllPlayers().forEach { player -> + try { + player.destroy() + } catch (e: Exception) { + // Log but don't crash on cleanup + } + } + PlayerRegistry.clear() + } + + AsyncFunction("play") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.play() + }.runOnQueue(Queues.MAIN) + + AsyncFunction("pause") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.pause() + }.runOnQueue(Queues.MAIN) + + AsyncFunction("mute") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.mute() + }.runOnQueue(Queues.MAIN) + + AsyncFunction("unmute") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.unmute() + }.runOnQueue(Queues.MAIN) + + AsyncFunction("seek") { nativeId: NativeId, time: Double -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.seek(time) + }.runOnQueue(Queues.MAIN) + + AsyncFunction("timeShift") { nativeId: NativeId, offset: Double -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.timeShift(offset) + }.runOnQueue(Queues.MAIN) + + AsyncFunction("destroy") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + if (player != null) { + // Note: MediaSession cleanup would need to be handled here + // For now, just destroy the player and remove from registry + player.destroy() + PlayerRegistry.unregister(nativeId) + } + }.runOnQueue(Queues.MAIN) - if (enableMediaSession) { - mediaSessionPlaybackManager.setupMediaSessionPlayback(nativeId) - } - } + AsyncFunction("setVolume") { nativeId: NativeId, volume: Double -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.volume = volume.toInt() + }.runOnQueue(Queues.MAIN) - /** - * Load the source of the given [nativeId] with `config` options from JS. - * @param nativeId Target player. - * @param sourceNativeId Target source. - */ - @ReactMethod - fun loadSource(nativeId: NativeId, sourceNativeId: String, promise: Promise) { - promise.unit.resolveOnUiThread { - getPlayer(nativeId, this@PlayerModule).load(getSource(sourceNativeId)) + AsyncFunction("getVolume") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.volume?.toDouble() } - } - /** - * Load the `offlineSourceConfig` for the player with [nativeId] and offline source module with `offlineModuleNativeId`. - * @param nativeId Target player. - * @param offlineContentManagerBridgeId Target offline module. - * @param options Source configuration options from JS. - */ - @ReactMethod - fun loadOfflineContent( - nativeId: NativeId, - offlineContentManagerBridgeId: String, - options: ReadableMap?, - promise: Promise, - ) { - promise.unit.resolveOnUiThread { - offlineModule - .getOfflineContentManagerBridgeOrNull(offlineContentManagerBridgeId) - ?.offlineContentManager - ?.offlineSourceConfig - ?.let { getPlayer(nativeId).load(it) } + AsyncFunction("currentTime") { nativeId: NativeId, mode: String? -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction when { + player == null -> null + mode == "relative" -> player.currentTime + player.playbackTimeOffsetToRelativeTime + mode == "absolute" -> player.currentTime + player.playbackTimeOffsetToAbsoluteTime + else -> player.currentTime + } } - } - /** - * Call `.unload()` on [nativeId]'s player. - * @param nativeId Target player Id. - */ - @ReactMethod - fun unload(nativeId: NativeId, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - unload() + AsyncFunction("isPlaying") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.isPlaying } - } - /** - * Call `.play()` on [nativeId]'s player. - * @param nativeId Target player Id. - */ - @ReactMethod - fun play(nativeId: NativeId, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - play() + AsyncFunction("isPaused") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.isPaused } - } - /** - * Call `.pause()` on [nativeId]'s player. - * @param nativeId Target player Id. - */ - @ReactMethod - fun pause(nativeId: NativeId, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - pause() + AsyncFunction("duration") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.duration } - } - /** - * Call `.seek(time:)` on [nativeId]'s player. - * @param nativeId Target player Id. - * @param time Seek time in seconds. - */ - @ReactMethod - fun seek(nativeId: NativeId, time: Double, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - seek(time) + AsyncFunction("isMuted") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.isMuted } - } - /** - * Call `.timeShift(offset:)` on [nativeId]'s player. - * @param nativeId Target player Id. - * @param offset Offset time in seconds. - */ - @ReactMethod - fun timeShift(nativeId: NativeId, offset: Double, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - timeShift(offset) - } - } + AsyncFunction("unload") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.unload() + }.runOnQueue(Queues.MAIN) - /** - * Call `.mute()` on [nativeId]'s player. - * @param nativeId Target player Id. - */ - @ReactMethod - fun mute(nativeId: NativeId, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - mute() + AsyncFunction("getTimeShift") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.timeShift } - } - /** - * Call `.unmute()` on [nativeId]'s player. - * @param nativeId Target player Id. - */ - @ReactMethod - fun unmute(nativeId: NativeId, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - unmute() + AsyncFunction("isLive") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.isLive } - } - /** - * Call `.destroy()` on [nativeId]'s player. - * @param nativeId Target player Id. - */ - @ReactMethod - fun destroy(nativeId: NativeId, promise: Promise) { - mediaSessionPlaybackManager.destroy(nativeId) - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - destroy() - players.remove(nativeId) + AsyncFunction("getMaxTimeShift") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.maxTimeShift } - } - /** - * Call `.setVolume(volume:)` on [nativeId]'s player. - * @param nativeId Target player Id. - * @param volume Volume level integer between 0 to 100. - */ - @ReactMethod - fun setVolume(nativeId: NativeId, volume: Int, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - this.volume = volume + AsyncFunction("getPlaybackSpeed") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.playbackSpeed?.toDouble() } - } - /** - * Resolve [nativeId]'s current volume. - * @param nativeId Target player Id. - * @param promise JS promise object. - */ - @ReactMethod - fun getVolume(nativeId: NativeId, promise: Promise) { - promise.int.resolveOnUiThreadWithPlayer(nativeId) { - volume - } - } + AsyncFunction("setPlaybackSpeed") { nativeId: NativeId, playbackSpeed: Double -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.playbackSpeed = playbackSpeed.toFloat() + }.runOnQueue(Queues.MAIN) - /** - * Resolve the source of [nativeId]'s player. - * @param nativeId Target player Id. - * @param promise JS promise object. - */ - @ReactMethod - fun source(nativeId: NativeId, promise: Promise) { - promise.map.nullable.resolveOnUiThreadWithPlayer(nativeId) { - source?.toJson() + AsyncFunction("isAd") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.isAd } - } - /** - * Resolve [nativeId]'s current playback time. - * @param nativeId Target player Id. - * @param promise JS promise object. - */ - @ReactMethod - fun currentTime(nativeId: NativeId, mode: String?, promise: Promise) { - promise.double.resolveOnUiThreadWithPlayer(nativeId) { - currentTime + when (mode) { - "relative" -> playbackTimeOffsetToRelativeTime - "absolute" -> playbackTimeOffsetToAbsoluteTime - else -> throw InvalidParameterException("Unknown mode $mode") - } - } - } + AsyncFunction("setMaxSelectableBitrate") { nativeId: NativeId, maxBitrate: Double -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.setMaxSelectableVideoBitrate(maxBitrate.toInt()) + }.runOnQueue(Queues.MAIN) - /** - * Resolve [nativeId]'s current source duration. - * @param nativeId Target player Id. - * @param promise JS promise object. - */ - @ReactMethod - fun duration(nativeId: NativeId, promise: Promise) { - promise.double.resolveOnUiThreadWithPlayer(nativeId) { - duration + AsyncFunction("isAirPlayActive") { _: String -> + // AirPlay is iOS-only, return null on Android + false } - } - /** - * Resolve [nativeId]'s current muted state. - * @param nativeId Target player Id. - * @param promise JS promise object. - */ - @ReactMethod - fun isMuted(nativeId: NativeId, promise: Promise) { - promise.bool.resolveOnUiThreadWithPlayer(nativeId) { - isMuted + AsyncFunction("isAirPlayAvailable") { _: String -> + // AirPlay is iOS-only, return null on Android + false } - } - /** - * Resolve [nativeId]'s current playing state. - * @param nativeId Target player Id. - * @param promise JS promise object. - */ - @ReactMethod - fun isPlaying(nativeId: NativeId, promise: Promise) { - promise.bool.resolveOnUiThreadWithPlayer(nativeId) { - isPlaying + AsyncFunction("isCastAvailable") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.isCastAvailable } - } - /** - * Resolve [nativeId]'s current paused state. - * @param nativeId Target player Id. - * @param promise JS promise object. - */ - @ReactMethod - fun isPaused(nativeId: NativeId, promise: Promise) { - promise.bool.resolveOnUiThreadWithPlayer(nativeId) { - isPaused + AsyncFunction("isCasting") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.isCasting } - } - /** - * Resolve [nativeId]'s current live state. - * @param nativeId Target player Id. - * @param promise JS promise object. - */ - @ReactMethod - fun isLive(nativeId: NativeId, promise: Promise) { - promise.bool.resolveOnUiThreadWithPlayer(nativeId) { - isLive - } - } + AsyncFunction("castVideo") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.castVideo() + }.runOnQueue(Queues.MAIN) - /** - * Resolve [nativeId]'s currently selected audio track. - * @param nativeId Target player Id. - * @param promise JS promise object. - */ - @ReactMethod - fun getAudioTrack(nativeId: NativeId, promise: Promise) { - promise.map.nullable.resolveOnUiThreadWithPlayer(nativeId) { - source?.selectedAudioTrack?.toJson() - } - } + AsyncFunction("castStop") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.castStop() + }.runOnQueue(Queues.MAIN) - /** - * Resolve [nativeId]'s player available audio tracks. - * @param nativeId Target player Id. - * @param promise JS promise object. - */ - @ReactMethod - fun getAvailableAudioTracks(nativeId: NativeId, promise: Promise) { - promise.array.resolveOnUiThreadWithPlayer(nativeId) { - source?.availableAudioTracks?.mapToReactArray { it.toJson() } ?: Arguments.createArray() - } - } + AsyncFunction("skipAd") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.skipAd() + }.runOnQueue(Queues.MAIN) - /** - * Set [nativeId]'s player audio track. - * @param nativeId Target player Id. - * @param trackIdentifier The audio track identifier. - * @param promise JS promise object. - */ - @ReactMethod - fun setAudioTrack(nativeId: NativeId, trackIdentifier: String, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - source?.setAudioTrack(trackIdentifier) + AsyncFunction("canPlayAtPlaybackSpeed") { _: String, _: Double -> + // This method is iOS-only, return false on Android + false } - } - /** - * Resolve [nativeId]'s currently selected subtitle track. - * @param nativeId Target player Id. - * @param promise JS promise object. - */ - @ReactMethod - fun getSubtitleTrack(nativeId: NativeId, promise: Promise) { - promise.map.nullable.resolveOnUiThreadWithPlayer(nativeId) { - source?.selectedSubtitleTrack?.toJson() + AsyncFunction("getAudioTrack") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.source?.selectedAudioTrack?.toJson() } - } - /** - * Resolve [nativeId]'s player available subtitle tracks. - * @param nativeId Target player Id. - * @param promise JS promise object. - */ - @ReactMethod - fun getAvailableSubtitles(nativeId: NativeId, promise: Promise) { - promise.array.resolveOnUiThreadWithPlayer(nativeId) { - source?.availableSubtitleTracks?.mapToReactArray { it.toJson() } ?: Arguments.createArray() + AsyncFunction("getAvailableAudioTracks") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.source?.availableAudioTracks?.map { it.toJson() } ?: emptyList() } - } - /** - * Set [nativeId]'s player subtitle track. - * @param nativeId Target player Id. - * @param trackIdentifier The subtitle track identifier. - * @param promise JS promise object. - */ - @ReactMethod - fun setSubtitleTrack(nativeId: NativeId, trackIdentifier: String?, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - source?.setSubtitleTrack(trackIdentifier) - } - } + AsyncFunction("setAudioTrack") { nativeId: NativeId, trackIdentifier: String -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.source?.setAudioTrack(trackIdentifier) + }.runOnQueue(Queues.MAIN) - /** - * Schedules an `AdItem` in the [nativeId]'s associated player. - * @param nativeId Target player id. - * @param adItemJson Json representation of the `AdItem` to be scheduled. - */ - @ReactMethod - fun scheduleAd(nativeId: NativeId, adItemJson: ReadableMap, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - scheduleAd(adItemJson.toAdItem() ?: throw IllegalArgumentException("invalid adItem")) + AsyncFunction("getSubtitleTrack") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.source?.selectedSubtitleTrack?.toJson() } - } - /** - * Skips the current ad in [nativeId]'s associated player. - * Has no effect if the current ad is not skippable or if no ad is being played back. - * @param nativeId Target player id. - */ - @ReactMethod - fun skipAd(nativeId: NativeId, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - skipAd() + AsyncFunction("getAvailableSubtitles") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.source?.availableSubtitleTracks?.map { it.toJson() } ?: emptyList() } - } - /** - * Returns `true` while an ad is being played back or when main content playback has been paused for ad playback. - * @param nativeId Target player id. - */ - @ReactMethod - fun isAd(nativeId: NativeId, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - isAd - } - } + AsyncFunction("setSubtitleTrack") { nativeId: NativeId, trackIdentifier: String? -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.source?.setSubtitleTrack(trackIdentifier) + }.runOnQueue(Queues.MAIN) - /** - * The current time shift of the live stream in seconds. This value is always 0 if the active [source] is not a - * live stream or there is no active playback session. - * @param nativeId Target player id. - */ - @ReactMethod - fun getTimeShift(nativeId: NativeId, promise: Promise) { - promise.double.resolveOnUiThreadWithPlayer(nativeId) { - timeShift + AsyncFunction("getVideoQuality") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.videoQuality?.toJson() } - } - /** - * The limit in seconds for time shifting. This value is either negative or 0 and it is always 0 if the active - * [source] is not a live stream or there is no active playback session. - * @param nativeId Target player id. - */ - @ReactMethod - fun getMaxTimeShift(nativeId: NativeId, promise: Promise) { - promise.double.resolveOnUiThreadWithPlayer(nativeId) { - maxTimeShift + AsyncFunction("getAvailableVideoQualities") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.availableVideoQualities?.map { it.toJson() } ?: emptyList() } - } - /** - * Sets the max selectable bitrate for the player. - * @param nativeId Target player id. - * @param maxSelectableBitrate The desired max bitrate limit. - */ - @ReactMethod - fun setMaxSelectableBitrate(nativeId: NativeId, maxSelectableBitrate: Int, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - setMaxSelectableVideoBitrate( - maxSelectableBitrate.takeUnless { it == -1 } ?: Integer.MAX_VALUE, - ) - } - } + AsyncFunction("setVideoQuality") { nativeId: NativeId, qualityId: String -> + val player = PlayerRegistry.getPlayer(nativeId) + player?.source?.setVideoQuality(qualityId) + }.runOnQueue(Queues.MAIN) - /** - * Returns the thumbnail image for the active `Source` at a certain time. - * @param nativeId Target player id. - * @param time Playback time for the thumbnail. - */ - @ReactMethod - fun getThumbnail(nativeId: NativeId, time: Double, promise: Promise) { - promise.map.nullable.resolveOnUiThreadWithPlayer(nativeId) { - source?.getThumbnail(time)?.toJson() + AsyncFunction("getThumbnail") { nativeId: NativeId, time: Double -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.getThumbnail(time)?.toJson() } - } + AsyncFunction("loadOfflineContent") { nativeId: NativeId, offlineContentManagerBridgeId: String, + options: Map?, -> + val player = PlayerRegistry.getPlayer(nativeId) ?: return@AsyncFunction + val offlineContentManagerBridge = appContext.registry.getModule() + ?.getOfflineContentManagerBridge(offlineContentManagerBridgeId) - /** - * Initiates casting the current video to a cast-compatible remote device. The user has to choose to which device it - * should be sent. - */ - @ReactMethod - fun castVideo(nativeId: NativeId, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - castVideo() - } - } + offlineContentManagerBridge?.offlineContentManager?.offlineSourceConfig?.let { + player.load(it) + } + }.runOnQueue(Queues.MAIN) - /** - * Stops casting the current video. Has no effect if [isCasting] is false. - */ - @ReactMethod - fun castStop(nativeId: NativeId, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - castStop() - } - } + AsyncFunction("scheduleAd") { nativeId: NativeId, adItemJson: Map -> + val player = PlayerRegistry.getPlayer(nativeId) + val adItem = adItemJson.toAdItem() + if (player != null && adItem != null) { + player.scheduleAd(adItem) + } + }.runOnQueue(Queues.MAIN) + + AsyncFunction("initializeWithConfig") { nativeId: NativeId, config: Map?, + networkNativeId: NativeId?, decoderNativeId: NativeId?, -> + initializePlayer(nativeId, config, networkNativeId, decoderNativeId, null) + }.runOnQueue(Queues.MAIN) + + AsyncFunction("initializeWithAnalyticsConfig") { nativeId: NativeId, analyticsConfigJson: Map, + config: Map?, networkNativeId: NativeId?, decoderNativeId: NativeId?, -> + initializePlayer(nativeId, config, networkNativeId, decoderNativeId, analyticsConfigJson) + }.runOnQueue(Queues.MAIN) + + AsyncFunction("loadSource") { nativeId: NativeId, sourceNativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + val source = appContext.registry.getModule()?.getSourceOrNull(sourceNativeId) + if (player != null && source != null) { + player.load(source) + } + }.runOnQueue(Queues.MAIN) - /** - * Whether casting to a cast-compatible remote device is available. [PlayerEvent.CastAvailable] signals when - * casting becomes available. - */ - @ReactMethod - fun isCastAvailable(nativeId: NativeId, promise: Promise) { - promise.bool.resolveOnUiThreadWithPlayer(nativeId) { - isCastAvailable + AsyncFunction("source") { nativeId: NativeId -> + val player = PlayerRegistry.getPlayer(nativeId) + return@AsyncFunction player?.source?.toJson() } } - /** - * Whether video is currently being casted to a remote device and not played locally. - */ - @ReactMethod - fun isCasting(nativeId: NativeId, promise: Promise) { - promise.bool.resolveOnUiThreadWithPlayer(nativeId) { - isCasting + private fun initializePlayer( + nativeId: NativeId, + config: Map?, + networkNativeId: NativeId?, + decoderNativeId: NativeId?, + analyticsConfigJson: Map?, + ) { + if (PlayerRegistry.hasPlayer(nativeId)) { + // Player already exists for this nativeId + return } - } - /** - * Resolve [nativeId]'s current video quality. - */ - @ReactMethod - fun getVideoQuality(nativeId: NativeId, promise: Promise) { - promise.map.nullable.resolveOnUiThreadWithPlayer(nativeId) { - source?.selectedVideoQuality?.toJson() - } - } + val playerConfig = config?.toPlayerConfig() ?: PlayerConfig() + val enableMediaSession = config?.getMap("mediaControlConfig") + ?.toMediaControlConfig()?.isEnabled ?: true - /** - * Resolve [nativeId]'s current available video qualities. - */ - @ReactMethod - fun getAvailableVideoQualities(nativeId: NativeId, promise: Promise) { - promise.array.resolveOnUiThreadWithPlayer(nativeId) { - source?.availableVideoQualities?.mapToReactArray { it.toJson() } ?: Arguments.createArray() + val networkConfig = networkNativeId?.let { id -> + appContext.registry.getModule()?.getConfig(id) + } + networkConfig?.let { + playerConfig.networkConfig = it } - } - /** - * Set [nativeId]'s player video quality. - * NOTE: ONLY available on Android. No effect on iOS and tvOS devices. - * @param nativeId Target player Id. - * @param qualityId The videoQualityId identifier. A list of currently available VideoQualitys can be retrieved via availableVideoQualities. To use automatic quality selection, Quality.AUTO_ID can be passed as qualityId. - * @param promise JS promise object. - */ - @ReactMethod - fun setVideoQuality(nativeId: NativeId, qualityId: String, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - source?.setVideoQuality(qualityId) + val decoderConfig = decoderNativeId?.let { + appContext.registry.getModule()?.getDecoderConfig(it) + } + if (decoderConfig != null) { + playerConfig.playbackConfig = playerConfig.playbackConfig.copy(decoderConfig = decoderConfig) } - } - /** - * Resolve [nativeId]'s current playback speed. - */ - @ReactMethod - fun getPlaybackSpeed(nativeId: NativeId, promise: Promise) { - promise.float.resolveOnUiThreadWithPlayer(nativeId) { - playbackSpeed + val applicationContext = appContext.reactContext?.applicationContext + ?: throw IllegalStateException("Application context is not available") + val analyticsConfig = analyticsConfigJson?.toAnalyticsConfig() + val defaultMetadata = analyticsConfigJson?.getMap("defaultMetadata")?.toAnalyticsDefaultMetadata() + + val player = if (analyticsConfig != null) { + Player.create( + context = applicationContext, + playerConfig = playerConfig, + analyticsConfig = analyticsConfig, + defaultMetadata = defaultMetadata ?: DefaultMetadata(), + ) + } else { + Player.create(applicationContext, playerConfig) } - } + PlayerRegistry.register(player, nativeId) - /** - * Sets playback speed for the player. - */ - @ReactMethod - fun setPlaybackSpeed(nativeId: NativeId, playbackSpeed: Float, promise: Promise) { - promise.unit.resolveOnUiThreadWithPlayer(nativeId) { - this.playbackSpeed = playbackSpeed + if (enableMediaSession) { + mediaSessionPlaybackManager.setupMediaSessionPlayback(nativeId) } } - private inline fun TPromise.resolveOnUiThreadWithPlayer( - nativeId: NativeId, - crossinline block: Player.() -> T, - ) = resolveOnUiThread { getPlayer(nativeId, this@PlayerModule).block() } + // CRITICAL: This method must remain available for cross-module access + fun getPlayerOrNull(nativeId: NativeId): Player? = PlayerRegistry.getPlayer(nativeId) } diff --git a/android/src/main/java/com/bitmovin/player/reactnative/PlayerRegistry.kt b/android/src/main/java/com/bitmovin/player/reactnative/PlayerRegistry.kt new file mode 100644 index 00000000..866ca4a0 --- /dev/null +++ b/android/src/main/java/com/bitmovin/player/reactnative/PlayerRegistry.kt @@ -0,0 +1,78 @@ +package com.bitmovin.player.reactnative + +import com.bitmovin.player.api.Player +import java.util.concurrent.ConcurrentHashMap + +/** + * Global registry for Player instances that allows static access from anywhere in native code + * without requiring access to the PlayerModule instance or Expo runtime. + */ +object PlayerRegistry { + private val players: Registry = ConcurrentHashMap() + + /** + * Register a player instance with the given native ID. + */ + @JvmStatic + fun register(player: Player, nativeId: NativeId) { + players[nativeId] = player + } + + /** + * Unregister a player instance with the given native ID. + */ + @JvmStatic + fun unregister(nativeId: NativeId) { + players.remove(nativeId) + } + + /** + * Get a player instance by native ID. + * Returns null if no player is registered with the given ID. + */ + @JvmStatic + fun getPlayer(nativeId: NativeId): Player? { + return players[nativeId] + } + + /** + * Get all registered player instances. + */ + @JvmStatic + fun getAllPlayers(): List { + return players.values.toList() + } + + /** + * Get all registered native IDs. + */ + @JvmStatic + fun getAllNativeIds(): List { + return players.keys.toList() + } + + /** + * Check if a player is registered with the given native ID. + */ + @JvmStatic + fun hasPlayer(nativeId: NativeId): Boolean { + return players.containsKey(nativeId) + } + + /** + * Clear all registered players. + * Note: This does not destroy the players, just removes them from the registry. + */ + @JvmStatic + fun clear() { + players.clear() + } + + /** + * Get the count of registered players. + */ + @JvmStatic + fun count(): Int { + return players.size + } +} \ No newline at end of file diff --git a/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerPackage.kt b/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerPackage.kt new file mode 100644 index 00000000..3afd6e7d --- /dev/null +++ b/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerPackage.kt @@ -0,0 +1,11 @@ +package com.bitmovin.player.reactnative + +import android.content.Context +import expo.modules.core.interfaces.Package +import expo.modules.core.interfaces.ReactActivityLifecycleListener + +class RNPlayerPackage : Package { + override fun createReactActivityLifecycleListeners(activityContext: Context): List { + return listOf(ActivityLifecycleListener()) + } +} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt b/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt index a8844675..45809ff7 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt @@ -2,6 +2,7 @@ package com.bitmovin.player.reactnative import android.annotation.SuppressLint import android.app.PictureInPictureParams +import android.content.Context import android.content.pm.PackageManager import android.content.res.Configuration import android.graphics.Rect @@ -9,6 +10,7 @@ import android.os.Build import android.util.Rational import android.view.View import android.view.ViewGroup +import android.view.WindowManager import android.widget.FrameLayout import androidx.annotation.RequiresApi import androidx.lifecycle.DefaultLifecycleObserver @@ -21,107 +23,113 @@ import com.bitmovin.player.api.event.Event import com.bitmovin.player.api.event.PlayerEvent import com.bitmovin.player.api.event.SourceEvent import com.bitmovin.player.api.ui.PlayerViewConfig -import com.bitmovin.player.api.ui.StyleConfig +import com.bitmovin.player.api.ui.ScalingMode +import com.bitmovin.player.api.ui.UiConfig import com.bitmovin.player.reactnative.converter.toJson -import com.bitmovin.player.reactnative.extensions.playerModule -import com.bitmovin.player.reactnative.ui.SubtitleViewConfig -import com.facebook.react.ReactActivity -import com.facebook.react.bridge.* -import com.facebook.react.uimanager.events.RCTEventEmitter -import kotlin.reflect.KClass - -private val EVENT_CLASS_TO_REACT_NATIVE_NAME_MAPPING = mapOf( - PlayerEvent.Error::class to "playerError", - PlayerEvent.Warning::class to "playerWarning", - PlayerEvent.Destroy::class to "destroy", - PlayerEvent.Muted::class to "muted", - PlayerEvent.Unmuted::class to "unmuted", - PlayerEvent.Ready::class to "ready", - PlayerEvent.Paused::class to "paused", - PlayerEvent.Play::class to "play", - PlayerEvent.Playing::class to "playing", - PlayerEvent.PlaybackFinished::class to "playbackFinished", - PlayerEvent.Seek::class to "seek", - PlayerEvent.Seeked::class to "seeked", - PlayerEvent.TimeShift::class to "timeShift", - PlayerEvent.TimeShifted::class to "timeShifted", - PlayerEvent.StallStarted::class to "stallStarted", - PlayerEvent.StallEnded::class to "stallEnded", - PlayerEvent.TimeChanged::class to "timeChanged", - SourceEvent.Load::class to "sourceLoad", - SourceEvent.Loaded::class to "sourceLoaded", - SourceEvent.Unloaded::class to "sourceUnloaded", - SourceEvent.Error::class to "sourceError", - SourceEvent.Warning::class to "sourceWarning", - SourceEvent.SubtitleTrackAdded::class to "subtitleAdded", - SourceEvent.SubtitleTrackChanged::class to "subtitleChanged", - SourceEvent.SubtitleTrackRemoved::class to "subtitleRemoved", - SourceEvent.AudioTrackAdded::class to "audioAdded", - SourceEvent.AudioTrackChanged::class to "audioChanged", - SourceEvent.AudioTrackRemoved::class to "audioRemoved", - SourceEvent.DownloadFinished::class to "downloadFinished", - SourceEvent.VideoDownloadQualityChanged::class to "videoDownloadQualityChanged", - PlayerEvent.AdBreakFinished::class to "adBreakFinished", - PlayerEvent.AdBreakStarted::class to "adBreakStarted", - PlayerEvent.AdClicked::class to "adClicked", - PlayerEvent.AdError::class to "adError", - PlayerEvent.AdFinished::class to "adFinished", - PlayerEvent.AdManifestLoad::class to "adManifestLoad", - PlayerEvent.AdManifestLoaded::class to "adManifestLoaded", - PlayerEvent.AdQuartile::class to "adQuartile", - PlayerEvent.AdScheduled::class to "adScheduled", - PlayerEvent.AdSkipped::class to "adSkipped", - PlayerEvent.AdStarted::class to "adStarted", - PlayerEvent.VideoPlaybackQualityChanged::class to "videoPlaybackQualityChanged", - PlayerEvent.CastStart::class to "castStart", - @Suppress("DEPRECATION") - PlayerEvent.CastPlaybackFinished::class to "castPlaybackFinished", - @Suppress("DEPRECATION") - PlayerEvent.CastPaused::class to "castPaused", - @Suppress("DEPRECATION") - PlayerEvent.CastPlaying::class to "castPlaying", - PlayerEvent.CastStarted::class to "castStarted", - PlayerEvent.CastAvailable::class to "castAvailable", - PlayerEvent.CastStopped::class to "castStopped", - PlayerEvent.CastWaitingForDevice::class to "castWaitingForDevice", - PlayerEvent.CastTimeUpdated::class to "castTimeUpdated", - PlayerEvent.CueEnter::class to "cueEnter", - PlayerEvent.CueExit::class to "cueExit", -) - -private val EVENT_CLASS_TO_REACT_NATIVE_NAME_MAPPING_UI = mapOf, String>( - PlayerEvent.PictureInPictureAvailabilityChanged::class to "pictureInPictureAvailabilityChanged", - PlayerEvent.PictureInPictureEnter::class to "pictureInPictureEnter", - PlayerEvent.PictureInPictureExit::class to "pictureInPictureExit", - PlayerEvent.FullscreenEnabled::class to "fullscreenEnabled", - PlayerEvent.FullscreenDisabled::class to "fullscreenDisabled", - PlayerEvent.FullscreenEnter::class to "fullscreenEnter", - PlayerEvent.FullscreenExit::class to "fullscreenExit", -) - -/** - * Native view wrapper for component instances. It both serves as the main view - * handled by RN (the actual player view is handled by the RNPlayerViewManager) and - * exposes player events as bubbling events. - */ +import com.bitmovin.player.reactnative.converter.toUserInterfaceType +import com.bitmovin.player.reactnative.ui.RNPictureInPictureHandler +import com.bitmovin.player.reactnative.util.NonFiniteSanitizer +import expo.modules.kotlin.AppContext +import expo.modules.kotlin.viewevent.EventDispatcher +import expo.modules.kotlin.viewevent.ViewEventCallback +import expo.modules.kotlin.views.ExpoView + @SuppressLint("ViewConstructor") -class RNPlayerView( - private val context: ReactContext, -) : FrameLayout(context) { - private val activityLifecycle = (context.currentActivity as? ReactActivity)?.lifecycle - ?: error("Trying to create an instance of ${this::class.simpleName} while not attached to a ReactActivity") +class RNPlayerView(context: Context, appContext: AppContext) : ExpoView(context, appContext) { + var playerView: PlayerView? = null + private set + private var subtitleView: SubtitleView? = null + private var playerContainer: FrameLayout? = null + var enableBackgroundPlayback: Boolean = false + private var scalingMode: ScalingMode? = null + private var requestedFullscreenValue: Boolean? = null + private var requestedPictureInPictureValue: Boolean? = null + private var fullscreenBridgeId: NativeId? = null + private var pictureInPictureConfig: PictureInPictureConfig? = null - /** - * Relays the provided set of events, emitted by the player, together with the associated name - * to the `eventOutput` callback. - */ - private var playerEventRelay: EventRelay = EventRelay( - EVENT_CLASS_TO_REACT_NATIVE_NAME_MAPPING, - ::emitEventFromPlayer, - ) + private val playerViewSourceRect = Rect() + private val playerViewLayoutListener = OnLayoutChangeListener { + _: View?, + left: Int, top: Int, right: Int, bottom: Int, + oldLeft: Int, oldRight: Int, oldTop: Int, oldBottom: Int, + -> + if (left != oldLeft || + right != oldRight || + top != oldTop || + bottom != oldBottom + ) { + playerView?.getGlobalVisibleRect(playerViewSourceRect) + applyPipConfig() + } + } - internal var enableBackgroundPlayback: Boolean = false - var playerInMediaSessionService: Player? = null + private val onBmpEvent by EventDispatcher() + private val onBmpPlayerActive by EventDispatcher() + private val onBmpPlayerInactive by EventDispatcher() + private val onBmpPlayerError by EventDispatcher() + private val onBmpPlayerWarning by EventDispatcher() + private val onBmpDestroy by EventDispatcher() + private val onBmpMuted by EventDispatcher() + private val onBmpUnmuted by EventDispatcher() + private val onBmpReady by EventDispatcher() + private val onBmpPaused by EventDispatcher() + private val onBmpPlay by EventDispatcher() + private val onBmpPlaying by EventDispatcher() + private val onBmpPlaybackFinished by EventDispatcher() + private val onBmpSeek by EventDispatcher() + private val onBmpSeeked by EventDispatcher() + private val onBmpTimeShift by EventDispatcher() + private val onBmpTimeShifted by EventDispatcher() + private val onBmpStallStarted by EventDispatcher() + private val onBmpStallEnded by EventDispatcher() + private val onBmpTimeChanged by EventDispatcher() + private val onBmpSourceLoad by EventDispatcher() + private val onBmpSourceLoaded by EventDispatcher() + private val onBmpSourceUnloaded by EventDispatcher() + private val onBmpSourceError by EventDispatcher() + private val onBmpSourceWarning by EventDispatcher() + private val onBmpAudioAdded by EventDispatcher() + private val onBmpAudioRemoved by EventDispatcher() + private val onBmpAudioChanged by EventDispatcher() + private val onBmpSubtitleAdded by EventDispatcher() + private val onBmpSubtitleRemoved by EventDispatcher() + private val onBmpSubtitleChanged by EventDispatcher() + private val onBmpDownloadFinished by EventDispatcher() + private val onBmpAdBreakFinished by EventDispatcher() + private val onBmpAdBreakStarted by EventDispatcher() + private val onBmpAdClicked by EventDispatcher() + private val onBmpAdError by EventDispatcher() + private val onBmpAdFinished by EventDispatcher() + private val onBmpAdManifestLoad by EventDispatcher() + private val onBmpAdManifestLoaded by EventDispatcher() + private val onBmpAdQuartile by EventDispatcher() + private val onBmpAdScheduled by EventDispatcher() + private val onBmpAdSkipped by EventDispatcher() + private val onBmpAdStarted by EventDispatcher() + private val onBmpVideoDownloadQualityChanged by EventDispatcher() + private val onBmpVideoPlaybackQualityChanged by EventDispatcher() + private val onBmpCastAvailable by EventDispatcher() + private val onBmpCastPaused by EventDispatcher() + private val onBmpCastPlaybackFinished by EventDispatcher() + private val onBmpCastPlaying by EventDispatcher() + private val onBmpCastStarted by EventDispatcher() + private val onBmpCastStart by EventDispatcher() + private val onBmpCastStopped by EventDispatcher() + private val onBmpCastTimeUpdated by EventDispatcher() + private val onBmpCastWaitingForDevice by EventDispatcher() + private val onBmpPlaybackSpeedChanged by EventDispatcher() + private val onBmpCueEnter by EventDispatcher() + private val onBmpCueExit by EventDispatcher() + + private val onBmpFullscreenEnabled by EventDispatcher() + private val onBmpFullscreenDisabled by EventDispatcher() + private val onBmpFullscreenEnter by EventDispatcher() + private val onBmpFullscreenExit by EventDispatcher() + private val onBmpPictureInPictureAvailabilityChanged by EventDispatcher() + private val onBmpPictureInPictureEnter by EventDispatcher() + private val onBmpPictureInPictureExit by EventDispatcher() + + private var playerInMediaSessionService: Player? = null private val activityLifecycleObserver = object : DefaultLifecycleObserver { override fun onStart(owner: LifecycleOwner) { @@ -155,7 +163,7 @@ class RNPlayerView( if (!enableBackgroundPlayback) { return } - if (context.playerModule?.mediaSessionPlaybackManager?.player != player) { + if (appContext.registry.getModule()?.mediaSessionPlaybackManager?.player != player) { return } @@ -164,136 +172,217 @@ class RNPlayerView( } } + private val viewAttachListener = object : OnAttachStateChangeListener { + override fun onViewAttachedToWindow(v: View) { + // do nothing + } + + override fun onViewDetachedFromWindow(v: View) { + clearPipAutoEnter() + } + } + + private val activityLifecycle: Lifecycle? = + (appContext.activityProvider?.currentActivity as? LifecycleOwner)?.lifecycle + init { // React Native has a bug that dynamically added views sometimes aren't laid out again properly. // Since we dynamically add and remove SurfaceView under the hood this caused the player // to suddenly not show the video anymore because SurfaceView was not laid out properly. // Bitmovin player issue: https://github.com/bitmovin/bitmovin-player-react-native/issues/180 // React Native layout issue: https://github.com/facebook/react-native/issues/17968 - getViewTreeObserver().addOnGlobalLayoutListener { requestLayout() } + viewTreeObserver.addOnGlobalLayoutListener { requestLayout() } - activityLifecycle.addObserver(activityLifecycleObserver) + activityLifecycle?.addObserver(activityLifecycleObserver) + addOnAttachStateChangeListener(viewAttachListener) } - /** - * Relays the provided set of events, emitted by the player view, together with the associated name - * to the `eventOutput` callback. - */ - private val viewEventRelay = EventRelay(EVENT_CLASS_TO_REACT_NATIVE_NAME_MAPPING_UI, ::emitEvent) + fun dispose() { + clearPipAutoEnter() + removeOnAttachStateChangeListener(viewAttachListener) + activityLifecycle?.removeObserver(activityLifecycleObserver) + playerView?.removeOnLayoutChangeListener(playerViewLayoutListener) + playerView?.onDestroy() + playerView = null + playerContainer?.let { container -> + (container.parent as? ViewGroup)?.removeView(container) + } + playerContainer = null + } - private var _playerView: PlayerView? = null - set(value) { - field = value - viewEventRelay.eventEmitter = field - playerEventRelay.eventEmitter = field?.player + private fun setPlayerView(playerView: PlayerView) { + // Remove existing playerView if it exists + this.playerView?.let { oldPlayerView -> + oldPlayerView.player?.let { + detachPlayerListeners(it) + } + (oldPlayerView.parent as? ViewGroup)?.removeView(oldPlayerView) + oldPlayerView.removeOnLayoutChangeListener(playerViewLayoutListener) + oldPlayerView.player = null } - /** - * Associated Bitmovin's `PlayerView`. - */ - val playerView: PlayerView? get() = _playerView + // Remove existing container if it exists + playerContainer?.let { oldContainer -> + (oldContainer.parent as? ViewGroup)?.removeView(oldContainer) + } - private var subtitleView: SubtitleView? = null - private val playerViewSourceRect = Rect() + // Create new container for the PlayerView + val newContainer = FrameLayout(context).apply { + layoutParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT, + ) + } - private val playerViewLayoutListener = OnLayoutChangeListener { - _: View?, - left: Int, top: Int, right: Int, bottom: Int, - oldLeft: Int, oldRight: Int, oldTop: Int, oldBottom: Int, - -> - if (left != oldLeft || - right != oldRight || - top != oldTop || - bottom != oldBottom - ) { - playerView?.getGlobalVisibleRect(playerViewSourceRect) - applyPipConfig() + // Add PlayerView to the container + (playerView.parent as ViewGroup?)?.removeView(playerView) + newContainer.addView( + playerView, + FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT, + ), + ) + + // Add container to the ExpoView with correct layout parameters + val containerLayoutParams = generateDefaultLayoutParams() + containerLayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT + containerLayoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT + addView(newContainer, 0, containerLayoutParams) + + this.playerView = playerView + this.playerContainer = newContainer + + playerView.addOnLayoutChangeListener(playerViewLayoutListener) + + scalingMode?.let { + playerView.scalingMode = it + } + fullscreenBridgeId?.let { + attachFullscreenBridge(it) + } + requestedFullscreenValue?.let { + setFullscreen(it) + } + requestedPictureInPictureValue?.let { + setPictureInPicture(it) } } - /** - * Handy property accessor for `playerView`'s player instance. - */ - var player: Player? - get() = playerView?.player - set(value) { - playerView?.player = value - playerEventRelay.eventEmitter = value + fun attachPlayer( + playerId: NativeId, + playerViewConfigWrapper: RNPlayerViewConfigWrapper?, + customMessageHandlerBridgeId: NativeId?, + enableBackgroundPlayback: Boolean, + isPictureInPictureEnabledOnPlayer: Boolean, + userInterfaceTypeName: String?, + ) { + val playerModule = appContext.registry.getModule() + // Player might not be initialized yet, this is a timing issue + // Return early without throwing to avoid crash + val player = playerModule?.getPlayerOrNull(playerId) ?: return + + if (playerView?.player == player) { + // Player is already attached to the PlayerView + return } - /** - * Configures the visual presentation and behaviour of the [playerView]. - */ - var config: RNPlayerViewConfigWrapper? = null - set(value) { - field = value - applySubtitleConfig() - applyPipConfig() + playerView?.player?.let { + detachPlayerListeners(it) } + attachPlayerListeners(player) + if (playerView != null) { + playerView?.player = player + } else { + this.enableBackgroundPlayback = enableBackgroundPlayback + val userInterfaceType = userInterfaceTypeName?.toUserInterfaceType() ?: UserInterfaceType.Bitmovin + val configuredPlayerViewConfig = playerViewConfigWrapper?.playerViewConfig ?: PlayerViewConfig() + + val currentActivity = appContext.activityProvider?.currentActivity + ?: throw IllegalStateException("Cannot create a PlayerView, because no activity is attached.") + val playerViewConfig: PlayerViewConfig = if (userInterfaceType != UserInterfaceType.Bitmovin) { + configuredPlayerViewConfig.copy(uiConfig = UiConfig.Disabled) + } else { + configuredPlayerViewConfig + } - /** - * Cleans up the resources and listeners produced by this view. - */ - fun dispose() { - clearPipAutoEnter() - activityLifecycle.removeObserver(activityLifecycleObserver) - - val playerView = _playerView ?: return - _playerView = null - // The `RNPlayerView` should not take care of the player lifecycle. - // As a different component is creating the player instance, the other component - // is responsible for destroying the player in the end. - playerView.player = null - playerView.onDestroy() - } + val newPlayerView = PlayerView(currentActivity, player, playerViewConfig) - /** - * Set the given `playerView` as child and start bubbling events. - * @param playerView Shared player view instance. - */ - fun setPlayerView(playerView: PlayerView) { - this.playerView?.let { currentPlayerView -> - (currentPlayerView.parent as? ViewGroup)?.removeView(currentPlayerView) - currentPlayerView.removeOnLayoutChangeListener(playerViewLayoutListener) + newPlayerView.layoutParams = LayoutParams( + LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT, + ) + + val isPictureInPictureEnabled = isPictureInPictureEnabledOnPlayer || + playerViewConfigWrapper?.pictureInPictureConfig?.isEnabled == true + + pictureInPictureConfig = playerViewConfigWrapper?.pictureInPictureConfig + + if (isPictureInPictureEnabled) { + newPlayerView.setPictureInPictureHandler(RNPictureInPictureHandler(currentActivity, player)) + } + setPlayerView(newPlayerView) + attachPlayerViewListeners(newPlayerView) + + val playerConfig = player.config + if (playerConfig.styleConfig.isUiEnabled && userInterfaceType == UserInterfaceType.Subtitle) { + appContext.activityProvider?.currentActivity?.let { activity -> + val subtitleView = SubtitleView(activity) + subtitleView.setPlayer(player) + playerViewConfigWrapper?.subtitleViewConfig?.let { + subtitleView.setPadding(it.paddingLeft, it.paddingTop, it.paddingRight, it.paddingBottom) + } + setSubtitleView(subtitleView) + } + } } - this._playerView = playerView - if (playerView.parent != this) { - (playerView.parent as ViewGroup?)?.removeView(playerView) - addView(playerView, 0) + customMessageHandlerBridgeId?.let { + appContext.registry.getModule()?.getInstance(it) + ?.let { customMessageHandlerBridge -> + playerView?.setCustomMessageHandler(customMessageHandlerBridge.customMessageHandler) + } } - playerView.addOnLayoutChangeListener(playerViewLayoutListener) } - /** - * Set the given `subtitleView` as a child - */ - fun setSubtitleView(subtitleView: SubtitleView) { + private fun setSubtitleView(subtitleView: SubtitleView) { this.subtitleView?.let { currentSubtitleView -> (currentSubtitleView.parent as? ViewGroup)?.removeView(currentSubtitleView) } this.subtitleView = subtitleView - applySubtitleConfig() - addView(subtitleView) - } - private fun applySubtitleConfig() { - config?.subtitleViewConfig?.let { - subtitleView?.setPadding(it.paddingLeft, it.paddingTop, it.paddingRight, it.paddingBottom) + // Add SubtitleView to the playerContainer instead of the ExpoView + // This ensures it's on top of the PlayerView + playerContainer?.let { container -> + val layoutParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT, + ) + container.addView(subtitleView, layoutParams) + subtitleView.bringToFront() // Ensure proper z-ordering } } - private var isCurrentActivityInPictureInPictureMode: Boolean = isInPictureInPictureMode() - private var isPictureInPictureAutoEnterEnabled: Boolean = false - private fun isPictureInPictureAvailable(): Boolean { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && context.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) } + private fun isInPictureInPictureMode(): Boolean { + val activity = appContext.activityProvider?.currentActivity ?: return false + return if (isPictureInPictureAvailable()) { + activity.isInPictureInPictureMode + } else { + false + } + } + + private var isCurrentActivityInPictureInPictureMode: Boolean = isInPictureInPictureMode() + private var isPictureInPictureAutoEnterEnabled: Boolean = false + @RequiresApi(Build.VERSION_CODES.O) private fun pictureInPictureParams(): PictureInPictureParams.Builder { val aspectRatio = - player?.playbackVideoData + playerView?.player?.playbackVideoData ?.let { Rational(it.width, it.height) } ?.let { rational -> val ratio = rational.toDouble() @@ -319,25 +408,17 @@ class RNPlayerView( return params } - fun pictureInPictureRequested(isPictureInPictureRequested: Boolean) { - val bitmovinView = playerView ?: return - if (bitmovinView.isPictureInPicture == isPictureInPictureRequested) return - if (!isPictureInPictureAvailable() || activityLifecycle.currentState != Lifecycle.State.RESUMED) return - - context.currentActivity?.enterPictureInPictureMode(pictureInPictureParams().build()) - } - private fun applyPipConfig() { - context.currentActivity?.let { activity -> + appContext.activityProvider?.currentActivity?.let { activity -> if (!isPictureInPictureAvailable() || Build.VERSION.SDK_INT < Build.VERSION_CODES.S || - _playerView == null + playerView == null ) { return } - val isAutoEnterConfigDisabled = config?.pictureInPictureConfig?.isEnabled != true || - config?.pictureInPictureConfig?.shouldEnterOnBackground != true + val isAutoEnterConfigDisabled = pictureInPictureConfig?.isEnabled != true || + pictureInPictureConfig?.shouldEnterOnBackground != true if (isAutoEnterConfigDisabled) { if (isPictureInPictureAutoEnterEnabled) { @@ -364,29 +445,24 @@ class RNPlayerView( return } - context.currentActivity?.setPictureInPictureParams( + appContext.activityProvider?.currentActivity?.setPictureInPictureParams( PictureInPictureParams.Builder().setAutoEnterEnabled(false).build(), ) isPictureInPictureAutoEnterEnabled = false } - private fun isInPictureInPictureMode(): Boolean { - val activity = context.currentActivity ?: return false - return if (isPictureInPictureAvailable()) { - activity.isInPictureInPictureMode - } else { - false - } - } - /** * Called whenever this view's activity configuration changes. */ override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) - if (isCurrentActivityInPictureInPictureMode != isInPictureInPictureMode()) { - isCurrentActivityInPictureInPictureMode = isInPictureInPictureMode() - onPictureInPictureModeChanged(isCurrentActivityInPictureInPictureMode, newConfig) + + val wasInPiP = isCurrentActivityInPictureInPictureMode + val nowInPiP = isInPictureInPictureMode() + + if (wasInPiP != nowInPiP) { + isCurrentActivityInPictureInPictureMode = nowInPiP + onPictureInPictureModeChanged(nowInPiP, newConfig) } } @@ -395,11 +471,326 @@ class RNPlayerView( newConfig: Configuration, ) { val playerView = playerView ?: return + playerView.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig) + if (isInPictureInPictureMode) { - playerView.enterPictureInPicture() + if (!playerView.isPictureInPicture) { + playerView.enterPictureInPicture() + } + + // Force layout update for PiP mode and ensure proper sizing + playerView.requestLayout() + requestLayout() + + // Additional PiP-specific layout handling + post { + val activity = appContext.activityProvider?.currentActivity + if (activity != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && + activity.isInPictureInPictureMode + ) { + // Get the actual PiP window dimensions from WindowManager + val windowManager = activity.getSystemService(Context.WINDOW_SERVICE) as WindowManager + val pipWidth: Int + val pipHeight: Int + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // Use WindowMetrics for API 30+ + val windowMetrics = windowManager.currentWindowMetrics + val windowBounds = windowMetrics.bounds + pipWidth = windowBounds.width() + pipHeight = windowBounds.height() + } else { + // Use deprecated Display.getSize() for older APIs + val displayMetrics = android.util.DisplayMetrics() + @Suppress("DEPRECATION") + windowManager.defaultDisplay.getMetrics(displayMetrics) + pipWidth = displayMetrics.widthPixels + pipHeight = displayMetrics.heightPixels + } + + // Force the ExpoView to be resized to PiP dimensions + // Preserve the original layout params type to avoid ClassCastException + layoutParams?.let { currentParams -> + currentParams.width = pipWidth + currentParams.height = pipHeight + // Re-assign to trigger layout update + layoutParams = currentParams + } + + // Ensure the ExpoView container is properly sized for PiP + measure( + MeasureSpec.makeMeasureSpec(pipWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(pipHeight, MeasureSpec.EXACTLY), + ) + layout(left, top, left + pipWidth, top + pipHeight) + + // Ensure the intermediate container is properly sized for PiP + playerContainer?.let { container -> + // Preserve the original layout params type for the container + container.layoutParams?.let { containerParams -> + containerParams.width = pipWidth + containerParams.height = pipHeight + container.layoutParams = containerParams + } + container.measure( + MeasureSpec.makeMeasureSpec(pipWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(pipHeight, MeasureSpec.EXACTLY), + ) + container.layout(0, 0, pipWidth, pipHeight) + } + + // Ensure the PlayerView is properly sized for PiP + playerView.layoutParams = FrameLayout.LayoutParams(pipWidth, pipHeight) + playerView.measure( + MeasureSpec.makeMeasureSpec(pipWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(pipHeight, MeasureSpec.EXACTLY), + ) + playerView.layout(0, 0, pipWidth, pipHeight) + + // Ensure the SubtitleView is properly sized for PiP + subtitleView?.let { subtitleView -> + subtitleView.layoutParams = FrameLayout.LayoutParams(pipWidth, pipHeight) + subtitleView.measure( + MeasureSpec.makeMeasureSpec(pipWidth, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(pipHeight, MeasureSpec.EXACTLY), + ) + subtitleView.layout(0, 0, pipWidth, pipHeight) + subtitleView.invalidate() + } + + // Try to force a redraw + playerView.invalidate() + playerContainer?.invalidate() + invalidate() + } + } } else { - playerView.exitPictureInPicture() + if (playerView.isPictureInPicture) { + playerView.exitPictureInPicture() + } + + // Restore full size layout when exiting PiP + post { + // Reset ExpoView to full size + layoutParams?.let { currentParams -> + currentParams.width = ViewGroup.LayoutParams.MATCH_PARENT + currentParams.height = ViewGroup.LayoutParams.MATCH_PARENT + layoutParams = currentParams + } + + // Reset intermediate container to full size + playerContainer?.let { container -> + container.layoutParams?.let { containerParams -> + containerParams.width = ViewGroup.LayoutParams.MATCH_PARENT + containerParams.height = ViewGroup.LayoutParams.MATCH_PARENT + container.layoutParams = containerParams + } + } + + // Reset PlayerView to full size + playerView.layoutParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT, + ) + + // Reset SubtitleView to full size + subtitleView?.let { subtitleView -> + subtitleView.layoutParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT, + ) + } + + // Force layout updates + measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY), + ) + layout(left, top, right, bottom) + + playerContainer?.let { container -> + container.measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY), + ) + container.layout(0, 0, width, height) + } + + playerView.measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY), + ) + playerView.layout(0, 0, width, height) + + // Ensure SubtitleView is properly measured and laid out when exiting PiP + subtitleView?.let { subtitleView -> + subtitleView.measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY), + ) + subtitleView.layout(0, 0, width, height) + subtitleView.invalidate() + } + } + } + } + + private fun attachPlayerViewListeners(playerView: PlayerView) { + playerView.on(PlayerEvent.FullscreenEnabled::class) { + onBmpFullscreenEnabled(it.toJson()) + } + playerView.on(PlayerEvent.FullscreenDisabled::class) { + onBmpFullscreenDisabled(it.toJson()) + } + playerView.on(PlayerEvent.FullscreenEnter::class) { + onBmpFullscreenEnter(it.toJson()) + } + playerView.on(PlayerEvent.FullscreenExit::class) { + onBmpFullscreenExit(it.toJson()) + } + playerView.on(PlayerEvent.PictureInPictureAvailabilityChanged::class) { + onBmpPictureInPictureAvailabilityChanged(it.toJson()) + } + playerView.on(PlayerEvent.PictureInPictureEnter::class) { + onBmpPictureInPictureEnter(it.toJson()) + } + playerView.on(PlayerEvent.PictureInPictureExit::class) { + onBmpPictureInPictureExit(it.toJson()) + } + } + + private var playerEventSubscriptions = mutableListOf>() + + private fun detachPlayerListeners(player: Player) { + playerEventSubscriptions.forEach { listener -> + player.off(listener) + } + playerEventSubscriptions.clear() + } + + private fun attachPlayerListeners(player: Player) { + playerEventSubscriptions = mutableListOf( + player.on { onEvent(onBmpPlayerActive, it.toJson()) }, + player.on { onEvent(onBmpPlayerInactive, it.toJson()) }, + player.on { onEvent(onBmpPlayerError, it.toJson()) }, + player.on { onEvent(onBmpPlayerWarning, it.toJson()) }, + player.on { onEvent(onBmpDestroy, it.toJson()) }, + player.on { onEvent(onBmpMuted, it.toJson()) }, + player.on { onEvent(onBmpUnmuted, it.toJson()) }, + player.on { onEvent(onBmpReady, it.toJson()) }, + player.on { onEvent(onBmpPaused, it.toJson()) }, + player.on { onEvent(onBmpPlay, it.toJson()) }, + player.on { onEvent(onBmpPlaying, it.toJson()) }, + player.on { onEvent(onBmpPlaybackFinished, it.toJson()) }, + player.on { onEvent(onBmpSeek, it.toJson()) }, + player.on { onEvent(onBmpSeeked, it.toJson()) }, + player.on { onEvent(onBmpTimeShift, it.toJson()) }, + player.on { onEvent(onBmpTimeShifted, it.toJson()) }, + player.on { onEvent(onBmpStallStarted, it.toJson()) }, + player.on { onEvent(onBmpStallEnded, it.toJson()) }, + player.on { onEvent(onBmpTimeChanged, it.toJson()) }, + player.on { onEvent(onBmpSourceLoad, it.toJson()) }, + player.on { onEvent(onBmpSourceLoaded, it.toJson()) }, + player.on { onEvent(onBmpSourceUnloaded, it.toJson()) }, + player.on { onEvent(onBmpSourceError, it.toJson()) }, + player.on { onEvent(onBmpSourceWarning, it.toJson()) }, + player.on { onEvent(onBmpAudioAdded, it.toJson()) }, + player.on { onEvent(onBmpAudioChanged, it.toJson()) }, + player.on { onEvent(onBmpAudioRemoved, it.toJson()) }, + player.on { onEvent(onBmpSubtitleAdded, it.toJson()) }, + player.on { onEvent(onBmpSubtitleChanged, it.toJson()) }, + player.on { onEvent(onBmpSubtitleRemoved, it.toJson()) }, + player.on { onEvent(onBmpDownloadFinished, it.toJson()) }, + player.on { onEvent(onBmpAdBreakFinished, it.toJson()) }, + player.on { onEvent(onBmpAdBreakStarted, it.toJson()) }, + player.on { onEvent(onBmpAdClicked, it.toJson()) }, + player.on { onEvent(onBmpAdError, it.toJson()) }, + player.on { onEvent(onBmpAdFinished, it.toJson()) }, + player.on { onEvent(onBmpAdManifestLoad, it.toJson()) }, + player.on { onEvent(onBmpAdManifestLoaded, it.toJson()) }, + player.on { onEvent(onBmpAdQuartile, it.toJson()) }, + player.on { onEvent(onBmpAdScheduled, it.toJson()) }, + player.on { onEvent(onBmpAdSkipped, it.toJson()) }, + player.on { onEvent(onBmpAdStarted, it.toJson()) }, + player.on { + onEvent( + onBmpVideoDownloadQualityChanged, + it.toJson(), + ) + }, + player.on { + onEvent( + onBmpVideoPlaybackQualityChanged, + it.toJson(), + ) + }, + player.on { onEvent(onBmpCastAvailable, it.toJson()) }, + player.on { onEvent(onBmpCastPaused, it.toJson()) }, + player.on { onEvent(onBmpCastPlaybackFinished, it.toJson()) }, + player.on { onEvent(onBmpCastPlaying, it.toJson()) }, + player.on { onEvent(onBmpCastStarted, it.toJson()) }, + player.on { onEvent(onBmpCastStart, it.toJson()) }, + player.on { onEvent(onBmpCastStopped, it.toJson()) }, + player.on { onEvent(onBmpCastTimeUpdated, it.toJson()) }, + player.on { onEvent(onBmpCastWaitingForDevice, it.toJson()) }, + player.on { onEvent(onBmpCueEnter, it.toJson()) }, + player.on { onEvent(onBmpCueExit, it.toJson()) }, + ) + } + + private fun onEvent(dispatcher: ViewEventCallback>, eventData: Map) { + val sanitized = NonFiniteSanitizer.sanitizeEventData(eventData) + dispatcher(sanitized) + onBmpEvent(sanitized) + } + + fun setFullscreen(isFullscreen: Boolean) { + requestedFullscreenValue = isFullscreen + playerView?.let { + if (it.isFullscreen == isFullscreen) return + if (isFullscreen) { + it.enterFullscreen() + } else { + it.exitFullscreen() + } + } + } + + fun setPictureInPicture(isPictureInPicture: Boolean) { + requestedPictureInPictureValue = isPictureInPicture + playerView?.let { + if (it.isPictureInPicture == isPictureInPicture) { + return + } + if (isPictureInPicture) { + it.enterPictureInPicture() + } else { + it.exitPictureInPicture() + } + } + } + + fun setScalingMode(scalingMode: String?) { + this.scalingMode = scalingMode?.let { ScalingMode.valueOf(it) } ?: ScalingMode.Fit + playerView?.scalingMode = this.scalingMode ?: ScalingMode.Fit + } + + fun attachFullscreenBridge(fullscreenBridgeId: NativeId) { + this.fullscreenBridgeId = fullscreenBridgeId + val playerView = playerView ?: return + appContext.registry.getModule()?.getInstance(fullscreenBridgeId) + ?.let { fullscreenBridge -> + playerView.setFullscreenHandler(fullscreenBridge) + } ?: throw IllegalArgumentException("Fullscreen bridge with ID $fullscreenBridgeId not found") + requestedFullscreenValue?.let { isFullscreen -> + playerView.let { + if (isFullscreen) { + it.enterFullscreen() + } else { + it.exitFullscreen() + } + } } } @@ -418,56 +809,10 @@ class RNPlayerView( layout(left, top, right, bottom) } } - - /** - * Emits a bubbling event with payload to js. - * @param name Native event name. - * @param event Optional js object to be sent as payload. - */ - private fun emitEvent(name: String, event: E) { - val payload = when (event) { - is PlayerEvent -> event.toJson() - is SourceEvent -> event.toJson() - else -> throw IllegalArgumentException() - } - - context - .getJSModule(RCTEventEmitter::class.java) - .receiveEvent(id, name, payload) - } - - /** - * Emits a bubbling event from the player with payload to js - * and emits it for "event" to support `onEvent` prop. - * @param name Native event name. - * @param event Optional js object to be sent as payload. - */ - private fun emitEventFromPlayer(name: String, event: E) { - emitEvent(name, event) - emitEvent("event", event) - } } -/** - * Representation of the React Native API `PlayerViewConfig` object. - * This is necessary as not all of its values can be directly mapped to the native `PlayerViewConfig`. - */ -data class RNPlayerViewConfigWrapper( - val playerViewConfig: PlayerViewConfig?, - val pictureInPictureConfig: PictureInPictureConfig?, - val subtitleViewConfig: SubtitleViewConfig?, -) - -data class RNStyleConfigWrapper( - val styleConfig: StyleConfig?, - val userInterfaceType: UserInterfaceType, -) - -enum class UserInterfaceType { - Bitmovin, Subtitle +private inline fun Player.on(noinline onEvent: (event: E) -> Unit): EventSubscription { + val eventSubscription = EventSubscription(E::class, onEvent) + this.on(eventSubscription.eventClass, eventSubscription.action) + return eventSubscription } - -/** - * Configuration type for picture in picture behaviors. - */ -data class PictureInPictureConfig(val isEnabled: Boolean, val shouldEnterOnBackground: Boolean) diff --git a/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt b/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt index dbd173fc..8bf3f08a 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt @@ -1,300 +1,115 @@ package com.bitmovin.player.reactnative -import android.os.Handler -import android.os.Looper -import android.util.Log -import android.view.ViewGroup.LayoutParams -import com.bitmovin.player.PlayerView -import com.bitmovin.player.SubtitleView -import com.bitmovin.player.api.ui.PlayerViewConfig -import com.bitmovin.player.api.ui.ScalingMode -import com.bitmovin.player.api.ui.UiConfig import com.bitmovin.player.reactnative.converter.toRNPlayerViewConfigWrapper -import com.bitmovin.player.reactnative.converter.toRNStyleConfigWrapperFromPlayerConfig -import com.bitmovin.player.reactnative.extensions.customMessageHandlerModule import com.bitmovin.player.reactnative.extensions.getBooleanOrNull -import com.bitmovin.player.reactnative.extensions.getModule -import com.bitmovin.player.reactnative.extensions.playerModule -import com.bitmovin.player.reactnative.ui.FullscreenHandlerModule -import com.bitmovin.player.reactnative.ui.RNPictureInPictureHandler -import com.facebook.react.bridge.* -import com.facebook.react.module.annotations.ReactModule -import com.facebook.react.uimanager.SimpleViewManager -import com.facebook.react.uimanager.ThemedReactContext -import com.facebook.react.uimanager.annotations.ReactProp -import java.security.InvalidParameterException - -private const val MODULE_NAME = "NativePlayerView" - -@ReactModule(name = MODULE_NAME) -class RNPlayerViewManager(context: ReactApplicationContext) : SimpleViewManager() { - /** - * Native component functions. - */ - enum class Commands(val command: String) { - ATTACH_PLAYER("attachPlayer"), - ATTACH_FULLSCREEN_BRIDGE("attachFullscreenBridge"), - SET_CUSTOM_MESSAGE_HANDLER_BRIDGE_ID("setCustomMessageHandlerBridgeId"), - SET_FULLSCREEN("setFullscreen"), - SET_SCALING_MODE("setScalingMode"), - SET_PICTURE_IN_PICTURE("setPictureInPicture"), - } - - /** - * Exported module name to JS. - */ - override fun getName() = MODULE_NAME - - private var customMessageHandlerBridgeId: NativeId? = null - private val handler = Handler(Looper.getMainLooper()) - private var context: ReactContext = context - - /** - * The component's native view factory. RN may call this method multiple times - * for each component instance. - */ - override fun createViewInstance(reactContext: ThemedReactContext): RNPlayerView { - this.context = reactContext - return RNPlayerView(reactContext) - } - - /** - * Called when the component's view gets detached from the view hierarchy. Useful to perform - * cleanups. - */ - override fun onDropViewInstance(view: RNPlayerView) { - super.onDropViewInstance(view) - view.dispose() - } - - /** - * A mapping between a event native identifier and its bubbled version that will - * be accessed from React. - */ - private val bubblingEventsMapping: Map = mapOf( - "event" to "onBmpEvent", - "playerError" to "onBmpPlayerError", - "playerWarning" to "onBmpPlayerWarning", - "destroy" to "onBmpDestroy", - "muted" to "onBmpMuted", - "unmuted" to "onBmpUnmuted", - "ready" to "onBmpReady", - "paused" to "onBmpPaused", - "play" to "onBmpPlay", - "playing" to "onBmpPlaying", - "playbackFinished" to "onBmpPlaybackFinished", - "seek" to "onBmpSeek", - "seeked" to "onBmpSeeked", - "timeShift" to "onBmpTimeShift", - "timeShifted" to "onBmpTimeShifted", - "stallStarted" to "onBmpStallStarted", - "stallEnded" to "onBmpStallEnded", - "timeChanged" to "onBmpTimeChanged", - "sourceLoad" to "onBmpSourceLoad", - "sourceLoaded" to "onBmpSourceLoaded", - "sourceUnloaded" to "onBmpSourceUnloaded", - "sourceError" to "onBmpSourceError", - "sourceWarning" to "onBmpSourceWarning", - "audioAdded" to "onBmpAudioAdded", - "audioChanged" to "onBmpAudioChanged", - "audioRemoved" to "onBmpAudioRemoved", - "subtitleAdded" to "onBmpSubtitleAdded", - "subtitleChanged" to "onBmpSubtitleChanged", - "subtitleRemoved" to "onBmpSubtitleRemoved", - "downloadFinished" to "onBmpDownloadFinished", - "videoDownloadQualityChanged" to "onBmpVideoDownloadQualityChanged", - "pictureInPictureAvailabilityChanged" to "onBmpPictureInPictureAvailabilityChanged", - "pictureInPictureEnter" to "onBmpPictureInPictureEnter", - "pictureInPictureExit" to "onBmpPictureInPictureExit", - "adBreakFinished" to "onBmpAdBreakFinished", - "adBreakStarted" to "onBmpAdBreakStarted", - "adClicked" to "onBmpAdClicked", - "adError" to "onBmpAdError", - "adFinished" to "onBmpAdFinished", - "adManifestLoad" to "onBmpAdManifestLoad", - "adManifestLoaded" to "onBmpAdManifestLoaded", - "adQuartile" to "onBmpAdQuartile", - "adScheduled" to "onBmpAdScheduled", - "adSkipped" to "onBmpAdSkipped", - "adStarted" to "onBmpAdStarted", - "videoPlaybackQualityChanged" to "onBmpVideoPlaybackQualityChanged", - "fullscreenEnabled" to "onBmpFullscreenEnabled", - "fullscreenDisabled" to "onBmpFullscreenDisabled", - "fullscreenEnter" to "onBmpFullscreenEnter", - "fullscreenExit" to "onBmpFullscreenExit", - "castStart" to "onBmpCastStart", - "castPlaybackFinished" to "onBmpCastPlaybackFinished", - "castPaused" to "onBmpCastPaused", - "castPlaying" to "onBmpCastPlaying", - "castStarted" to "onBmpCastStarted", - "castAvailable" to "onBmpCastAvailable", - "castStopped" to "onBmpCastStopped", - "castWaitingForDevice" to "onBmpCastWaitingForDevice", - "castTimeUpdated" to "onBmpCastTimeUpdated", - "cueEnter" to "onBmpCueEnter", - "cueExit" to "onBmpCueExit", - ) - - /** - * Component's event registry. Bubbling events are directly mapped to react props. No - * need to use proxy functions or `NativeEventEmitter`. - * @return map between event names (sent from native code) to js props. - */ - override fun getExportedCustomBubblingEventTypeConstants(): MutableMap = - bubblingEventsMapping.entries.associate { - it.key to mapOf( - "phasedRegistrationNames" to mapOf("bubbled" to it.value), - ) - }.toMutableMap() - - /** - * Component's command registry. They enable granular control over - * instances of a certain native component from js and give the ability - * to call 'functions' on them. - * @return map between names (used in js) and command ids (used in native code). - */ - override fun getCommandsMap(): Map = Commands.values().associate { - it.command to it.ordinal - } - - /** - * Callback triggered in response to command dispatches from the js side. - * @param view Root native view of the targeted component. - * @param commandId Command number identifier. It's a number even though RN sends it as a string. - * @param args Arguments list sent from the js side. - */ - override fun receiveCommand(view: RNPlayerView, commandId: String?, args: ReadableArray?) { - fun Int.toCommand(): Commands? = Commands.values().getOrNull(this) - val command = commandId?.toInt()?.toCommand() ?: throw IllegalArgumentException( - "The received command is not supported by the Bitmovin Player View", - ) - - fun T?.require(): T = this ?: throw InvalidParameterException("Missing parameter") - when (command) { - Commands.ATTACH_PLAYER -> attachPlayer(view, args?.getString(1).require(), args?.getMap(2)) - Commands.ATTACH_FULLSCREEN_BRIDGE -> attachFullscreenBridge(view, args?.getString(1).require()) - Commands.SET_CUSTOM_MESSAGE_HANDLER_BRIDGE_ID -> setCustomMessageHandlerBridgeId( - view, - args?.getString(1).require(), - ) - - Commands.SET_FULLSCREEN -> setFullscreen(view, args?.getBoolean(1).require()) - Commands.SET_SCALING_MODE -> setScalingMode(view, args?.getString(1).require()) - Commands.SET_PICTURE_IN_PICTURE -> setPictureInPicture(view, args?.getBoolean(1).require()) - } - } - - @ReactProp(name = "config") - fun setConfig(view: RNPlayerView, config: ReadableMap?) { - view.config = config?.toRNPlayerViewConfigWrapper() - } - - private fun attachFullscreenBridge(view: RNPlayerView, fullscreenBridgeId: NativeId) { - handler.postAndLogException { - view.playerView?.setFullscreenHandler( - context.getModule()?.getInstance(fullscreenBridgeId), - ) - } - } - - private fun setFullscreen(view: RNPlayerView, isFullscreenRequested: Boolean) { - handler.postAndLogException { - val playerView = view.playerView ?: return@postAndLogException - if (playerView.isFullscreen == isFullscreenRequested) return@postAndLogException - if (isFullscreenRequested) { - playerView.enterFullscreen() - } else { - playerView.exitFullscreen() +import com.bitmovin.player.reactnative.extensions.getMap +import com.bitmovin.player.reactnative.extensions.getString +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition + +class RNPlayerViewManager : Module() { + override fun definition() = ModuleDefinition { + Name("RNPlayerViewManager") + + View(RNPlayerView::class) { + Prop("config") { view: RNPlayerView, playerInfo: Map? -> + val playerId = playerInfo?.get("playerId") as? String + ?: throw IllegalArgumentException("Player info must contain 'playerId' field") + val customMessageHandlerBridgeId = playerInfo.getString("customMessageHandlerBridgeId") + val enableBackgroundPlayback = playerInfo.getBooleanOrNull("enableBackgroundPlayback") ?: false + val isPictureInPictureEnabledOnPlayer = + playerInfo.getBooleanOrNull("isPictureInPictureEnabledOnPlayer") ?: false + val userInterfaceTypeName = playerInfo.getString("userInterfaceTypeName") + val playerViewConfigWrapper = playerInfo.getMap("playerViewConfig")?.toRNPlayerViewConfigWrapper() + view.attachPlayer( + playerId, + playerViewConfigWrapper, + customMessageHandlerBridgeId, + enableBackgroundPlayback, + isPictureInPictureEnabledOnPlayer, + userInterfaceTypeName, + ) } - } - } - - private fun setPictureInPicture(view: RNPlayerView, isPictureInPictureRequested: Boolean) { - handler.postAndLogException { - view.pictureInPictureRequested(isPictureInPictureRequested) - } - } - - private fun setScalingMode(view: RNPlayerView, scalingMode: String) { - handler.postAndLogException { - view.playerView?.scalingMode = ScalingMode.valueOf(scalingMode) - } - } - private fun setCustomMessageHandlerBridgeId(view: RNPlayerView, customMessageHandlerBridgeId: NativeId) { - this.customMessageHandlerBridgeId = customMessageHandlerBridgeId - attachCustomMessageHandlerBridge(view) - } - - private fun attachCustomMessageHandlerBridge(view: RNPlayerView) { - view.playerView?.setCustomMessageHandler( - context.customMessageHandlerModule - ?.getInstance(customMessageHandlerBridgeId) - ?.customMessageHandler, - ) - } - - /** - * Set the `Player` instance for the target view using `playerId`. - * @param view Target `RNPlayerView`. - * @param playerId `Player` instance id inside `PlayerModule`'s registry. - */ - private fun attachPlayer(view: RNPlayerView, playerId: NativeId, playerConfig: ReadableMap?) { - handler.postAndLogException { - val player = playerId.let { context.playerModule?.getPlayerOrNull(it) } - ?: throw InvalidParameterException("Cannot create a PlayerView, invalid playerId was passed: $playerId") - val playbackConfig = playerConfig?.getMap("playbackConfig") - val isPictureInPictureEnabled = view.config?.pictureInPictureConfig?.isEnabled == true || - playbackConfig?.getBooleanOrNull("isPictureInPictureEnabled") == true - view.enableBackgroundPlayback = playbackConfig?.getBooleanOrNull("isBackgroundPlaybackEnabled") == true - - val rnStyleConfigWrapper = playerConfig?.toRNStyleConfigWrapperFromPlayerConfig() - val configuredPlayerViewConfig = view.config?.playerViewConfig ?: PlayerViewConfig() - - if (view.playerView != null) { - view.player = player - } else { - // PlayerView has to be initialized with Activity context - val currentActivity = context.currentActivity - ?: throw IllegalStateException("Cannot create a PlayerView, because no activity is attached.") - val userInterfaceType = rnStyleConfigWrapper?.userInterfaceType ?: UserInterfaceType.Bitmovin - val playerViewConfig: PlayerViewConfig = if (userInterfaceType != UserInterfaceType.Bitmovin) { - configuredPlayerViewConfig.copy(uiConfig = UiConfig.Disabled) - } else { - configuredPlayerViewConfig - } + Prop("scalingMode") { view: RNPlayerView, scalingMode: String? -> + view.setScalingMode(scalingMode) + } - val playerView = PlayerView(currentActivity, player, playerViewConfig) + Prop("isFullscreenRequested") { view: RNPlayerView, isFullscreen: Boolean -> + view.setFullscreen(isFullscreen) + } - playerView.layoutParams = LayoutParams( - LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT, - ) - if (isPictureInPictureEnabled) { - playerView.setPictureInPictureHandler(RNPictureInPictureHandler(currentActivity, player)) - } - view.setPlayerView(playerView) - attachCustomMessageHandlerBridge(view) + Prop("isPictureInPictureRequested") { view: RNPlayerView, isPictureInPicture: Boolean -> + view.setPictureInPicture(isPictureInPicture) } - if (rnStyleConfigWrapper?.styleConfig?.isUiEnabled != false && - rnStyleConfigWrapper?.userInterfaceType == UserInterfaceType.Subtitle - ) { - context.currentActivity?.let { activity -> - val subtitleView = SubtitleView(activity) - subtitleView.setPlayer(player) - view.setSubtitleView(subtitleView) - } + Prop("fullscreenBridgeId") { view: RNPlayerView, fullscreenBridgeId: String -> + view.attachFullscreenBridge(fullscreenBridgeId) } - } - } - /** Post and log any exceptions instead of crashing the app. */ - private inline fun Handler.postAndLogException(crossinline block: () -> Unit) = post { - try { - block() - } catch (e: Exception) { - Log.e(MODULE_NAME, "Error while executing command", e) + Events( + "onBmpEvent", + "onBmpPlayerError", + "onBmpPlayerWarning", + "onBmpDestroy", + "onBmpMuted", + "onBmpUnmuted", + "onBmpReady", + "onBmpPaused", + "onBmpPlay", + "onBmpPlaying", + "onBmpPlaybackFinished", + "onBmpSeek", + "onBmpSeeked", + "onBmpTimeShift", + "onBmpTimeShifted", + "onBmpStallStarted", + "onBmpStallEnded", + "onBmpTimeChanged", + "onBmpSourceLoad", + "onBmpSourceLoaded", + "onBmpSourceUnloaded", + "onBmpSourceError", + "onBmpSourceWarning", + "onBmpAudioAdded", + "onBmpAudioChanged", + "onBmpAudioRemoved", + "onBmpSubtitleAdded", + "onBmpSubtitleChanged", + "onBmpSubtitleRemoved", + "onBmpDownloadFinished", + "onBmpVideoDownloadQualityChanged", + "onBmpPictureInPictureAvailabilityChanged", + "onBmpPictureInPictureEnter", + "onBmpPictureInPictureExit", + "onBmpAdBreakFinished", + "onBmpAdBreakStarted", + "onBmpAdClicked", + "onBmpAdError", + "onBmpAdFinished", + "onBmpAdManifestLoad", + "onBmpAdManifestLoaded", + "onBmpAdQuartile", + "onBmpAdScheduled", + "onBmpAdSkipped", + "onBmpAdStarted", + "onBmpVideoPlaybackQualityChanged", + "onBmpFullscreenEnabled", + "onBmpFullscreenDisabled", + "onBmpFullscreenEnter", + "onBmpFullscreenExit", + "onBmpCastStart", + "onBmpCastPlaybackFinished", + "onBmpCastPaused", + "onBmpCastPlaying", + "onBmpCastStarted", + "onBmpCastAvailable", + "onBmpCastStopped", + "onBmpCastWaitingForDevice", + "onBmpCastTimeUpdated", + "onBmpCueEnter", + "onBmpCueExit", + ) } } } diff --git a/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewPackage.kt b/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewPackage.kt deleted file mode 100644 index 7d3ab01f..00000000 --- a/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewPackage.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.bitmovin.player.reactnative - -import android.view.View -import com.bitmovin.player.reactnative.ui.CustomMessageHandlerModule -import com.bitmovin.player.reactnative.ui.FullscreenHandlerModule -import com.facebook.react.ReactPackage -import com.facebook.react.bridge.* -import com.facebook.react.uimanager.ReactShadowNode -import com.facebook.react.uimanager.ViewManager - -/** - * React package registry. - */ -class RNPlayerViewPackage : ReactPackage { - /** - * Register `RNPlayerViewManager` as a base react native module. This allows - * accessing methods on `NativePlayerView` on the js side. - */ - override fun createNativeModules(reactContext: ReactApplicationContext): MutableList { - return mutableListOf( - OfflineModule(reactContext), - UuidModule(reactContext), - PlayerModule(reactContext), - SourceModule(reactContext), - DrmModule(reactContext), - PlayerAnalyticsModule(reactContext), - RNPlayerViewManager(reactContext), - FullscreenHandlerModule(reactContext), - CustomMessageHandlerModule(reactContext), - BitmovinCastManagerModule(reactContext), - BufferModule(reactContext), - NetworkModule(reactContext), - DebugModule(reactContext), - ) - } - - /** - * Register `RNPlayerViewManager` as a view manager. This allows creating - * native component instances with `` on the js - * side. - */ - override fun createViewManagers( - reactContext: ReactApplicationContext, - ): MutableList>> { - return mutableListOf(RNPlayerViewManager(reactContext)) - } -} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/ResultWaiter.kt b/android/src/main/java/com/bitmovin/player/reactnative/ResultWaiter.kt new file mode 100644 index 00000000..b26c6128 --- /dev/null +++ b/android/src/main/java/com/bitmovin/player/reactnative/ResultWaiter.kt @@ -0,0 +1,58 @@ +package com.bitmovin.player.reactnative + +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger + +/** + * Lets native code synchronously wait for a value that will be supplied + * later (typically from JavaScript). Thread-safe, generic, timeout-aware. + * + * val (id, wait) = boolWaiter.make(250) // 250 ms + * sendEvent("...", mapOf("id" to id)) + * val answer = wait() ?: false // fallback on timeout + */ +class ResultWaiter { + + private data class Entry( + val latch: CountDownLatch = CountDownLatch(1), + @Volatile var value: V? = null, + ) + + private val nextId = AtomicInteger() + private val table = ConcurrentHashMap>() + + /** + * Registers a new waiter and returns: + * • id – unique request handle + * • wait – blocking lambda that returns null on timeout + * + * @param timeoutMs max time the caller is willing to block + */ + fun make(timeoutMs: Long): Pair T?> { + val id = nextId.incrementAndGet() + val entry = Entry() + table[id] = entry + + val waitFn = { + entry.latch.await(timeoutMs, TimeUnit.MILLISECONDS) + table.remove(id) // GC once done + entry.value + } + + return id to waitFn + } + + /** Completes the waiter if it exists; does nothing otherwise. */ + fun complete(id: Int, value: T) { + table[id]?.let { + it.value = value + it.latch.countDown() + } + } + + fun clear() { + table.clear() + } +} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/SourceModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/SourceModule.kt index 82ffe116..c788e28d 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/SourceModule.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/SourceModule.kt @@ -1,188 +1,114 @@ package com.bitmovin.player.reactnative -import android.util.Log import com.bitmovin.player.api.analytics.create import com.bitmovin.player.api.source.Source import com.bitmovin.player.reactnative.converter.toAnalyticsSourceMetadata import com.bitmovin.player.reactnative.converter.toJson import com.bitmovin.player.reactnative.converter.toSourceConfig -import com.bitmovin.player.reactnative.extensions.toMap -import com.bitmovin.player.reactnative.extensions.toReadableMap -import com.facebook.react.bridge.Promise -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactMethod -import com.facebook.react.bridge.ReadableMap -import com.facebook.react.module.annotations.ReactModule -import java.security.InvalidParameterException - -private const val MODULE_NAME = "SourceModule" - -@ReactModule(name = MODULE_NAME) -class SourceModule(context: ReactApplicationContext) : BitmovinBaseModule(context) { - /** - * In-memory mapping from `nativeId`s to `Source` instances. - */ - private val sources: Registry = mutableMapOf() +import expo.modules.kotlin.exception.CodedException +import expo.modules.kotlin.modules.Module +import expo.modules.kotlin.modules.ModuleDefinition +class SourceModule : Module() { /** - * JS exported module name. + * In-memory mapping from [NativeId]s to [Source] instances. + * This must match the Registry pattern from legacy SourceModule */ - override fun getName() = MODULE_NAME + private val sources: Registry = mutableMapOf() - /** - * Fetches the [Source] instance associated with [nativeId] from internal sources or null. - */ - fun getSourceOrNull(nativeId: NativeId): Source? = sources[nativeId] + override fun definition() = ModuleDefinition { + Name("SourceModule") - /** - * Creates a new `Source` instance inside the internal sources using the provided - * `config` and `analyticsSourceMetadata` object as well as an initialized DRM configuration ID. - * @param nativeId ID to be associated with the `Source` instance. - * @param drmNativeId ID of the DRM config to use. - * @param config `SourceConfig` object received from JS. - * @param sourceRemoteControlConfig `SourceRemoteControlConfig` object received from JS. Not supported on Android. - * @param analyticsSourceMetadata `SourceMetadata` object received from JS. - */ - @ReactMethod - fun initWithAnalyticsConfig( - nativeId: NativeId, - drmNativeId: NativeId?, - config: ReadableMap?, - sourceRemoteControlConfig: ReadableMap?, - analyticsSourceMetadata: ReadableMap, - promise: Promise, - ) = initializeSource(nativeId, drmNativeId, config, analyticsSourceMetadata, promise) + OnCreate { + // Module initialization + } - /** - * Creates a new `Source` instance inside the internal sources using the provided - * `config` object and an initialized DRM configuration ID. - * @param nativeId ID to be associated with the `Source` instance. - * @param drmNativeId ID of the DRM config to use. - * @param config `SourceConfig` object received from JS. - * @param sourceRemoteControlConfig `SourceRemoteControlConfig` object received from JS. Not supported on Android. - */ - @ReactMethod - fun initWithConfig( - nativeId: NativeId, - drmNativeId: NativeId?, - config: ReadableMap?, - sourceRemoteControlConfig: ReadableMap?, - promise: Promise, - ) = initializeSource(nativeId, drmNativeId, config, analyticsSourceMetadata = null, promise) + OnDestroy { + // Clean up sources + sources.clear() + } - private fun initializeSource( - nativeId: NativeId, - drmNativeId: NativeId?, - config: ReadableMap?, - analyticsSourceMetadata: ReadableMap?, - promise: Promise, - ) = promise.unit.resolveOnUiThread { - if (sources.containsKey(nativeId)) { - if (drmNativeId != null || config != null || analyticsSourceMetadata != null) { - Log.w("BitmovinSourceModule", "Cannot reconfigure an existing source") - } - return@resolveOnUiThread // key can be reused to access the same native instance (see NativeInstanceConfig) + AsyncFunction("initializeWithConfig") { nativeId: NativeId, drmNativeId: NativeId?, + config: Map?, sourceRemoteControlConfig: Map?, -> + initializeSource(nativeId, drmNativeId, config, sourceRemoteControlConfig, null) } - val drmConfig = drmNativeId?.let { drmModule.getConfig(it) } - val sourceConfig = config?.toSourceConfig() ?: throw InvalidParameterException("Invalid SourceConfig") - val sourceMetadata = analyticsSourceMetadata?.toAnalyticsSourceMetadata() - sourceConfig.drmConfig = drmConfig - sources[nativeId] = if (sourceMetadata == null) { - Source.create(sourceConfig) - } else { - Source.create(sourceConfig, sourceMetadata) + + AsyncFunction("initializeWithAnalyticsConfig") { nativeId: NativeId, drmNativeId: NativeId?, + config: Map?, sourceRemoteControlConfig: Map?, + analyticsSourceMetadata: Map?, -> + initializeSource(nativeId, drmNativeId, config, sourceRemoteControlConfig, analyticsSourceMetadata) } - } - /** - * Removes the `Source` instance associated with `nativeId` from the internal sources. - * @param nativeId `Source` to be disposed. - */ - @ReactMethod - fun destroy(nativeId: NativeId, promise: Promise) { - promise.unit.resolveOnUiThreadWithSource(nativeId) { + AsyncFunction("destroy") { nativeId: NativeId -> sources.remove(nativeId) } - } - /** - * Whether `nativeId` source is currently attached to a player instance. - * @param nativeId Source `nativeId`. - * @param promise: JS promise object. - */ - @ReactMethod - fun isAttachedToPlayer(nativeId: NativeId, promise: Promise) { - promise.bool.resolveOnUiThreadWithSource(nativeId) { - isAttachedToPlayer + AsyncFunction("isAttachedToPlayer") { nativeId: NativeId -> + sources[nativeId]?.isAttachedToPlayer } - } - /** - * Whether `nativeId` source is currently active in a `Player`. - * @param nativeId Source `nativeId`. - * @param promise: JS promise object. - */ - @ReactMethod - fun isActive(nativeId: NativeId, promise: Promise) { - promise.bool.resolveOnUiThreadWithSource(nativeId) { - isActive + AsyncFunction("isActive") { nativeId: NativeId -> + sources[nativeId]?.isActive } - } - /** - * The duration of `nativeId` source in seconds. - */ - @ReactMethod - fun duration(nativeId: NativeId, promise: Promise) { - promise.double.resolveOnUiThreadWithSource(nativeId) { - duration + AsyncFunction("duration") { nativeId: NativeId -> + sources[nativeId]?.duration } - } - /** - * The current loading state of `nativeId` source. - */ - @ReactMethod - fun loadingState(nativeId: NativeId, promise: Promise) { - promise.int.resolveOnUiThreadWithSource(nativeId) { - loadingState.ordinal + AsyncFunction("loadingState") { nativeId: NativeId -> + sources[nativeId]?.loadingState?.name } - } - /** - * Metadata for the currently loaded `nativeId` source. - */ - @ReactMethod - fun getMetadata(nativeId: NativeId, promise: Promise) { - promise.map.nullable.resolveOnUiThreadWithSource(nativeId) { - config.metadata?.toReadableMap() + AsyncFunction("getMetadata") { nativeId: NativeId -> + sources[nativeId]?.config?.metadata } - } - /** - * Set the metadata for a loaded `nativeId` source. - */ - @ReactMethod - fun setMetadata(nativeId: NativeId, metadata: ReadableMap?, promise: Promise) { - promise.unit.resolveOnUiThreadWithSource(nativeId) { - config.metadata = metadata?.toMap() + AsyncFunction("setMetadata") { nativeId: NativeId, metadata: Map? -> + sources[nativeId]?.config?.metadata = metadata?.mapValues { it.value.toString() } } - } - /** - * Returns the thumbnail image for the `Source` at a certain time. - * @param nativeId Target player id. - * @param time Playback time for the thumbnail. - */ - @ReactMethod - fun getThumbnail(nativeId: NativeId, time: Double, promise: Promise) { - promise.map.nullable.resolveOnUiThreadWithSource(nativeId) { - getThumbnail(time)?.toJson() + AsyncFunction("getThumbnail") { nativeId: NativeId, time: Double -> + sources[nativeId]?.getThumbnail(time)?.toJson() } } - private inline fun TPromise.resolveOnUiThreadWithSource( + private fun initializeSource( nativeId: NativeId, - crossinline block: Source.() -> T, - ) = resolveOnUiThread { getSource(nativeId, this@SourceModule).block() } + drmNativeId: NativeId?, + config: Map?, + sourceRemoteControlConfig: Map?, + analyticsSourceMetadata: Map?, + ) { + if (sources.containsKey(nativeId)) { + return // Source already exists + } + + val sourceConfig = config?.toSourceConfig() + ?: throw SourceException.InvalidSourceConfig() + + // Get DRM config if provided + sourceConfig.drmConfig = appContext.registry.getModule()?.getConfig(drmNativeId) + + val sourceMetadata = analyticsSourceMetadata?.toAnalyticsSourceMetadata() + try { + sources[nativeId] = if (sourceMetadata != null) { + Source.create(sourceConfig, sourceMetadata) + } else { + Source.create(sourceConfig) + } + } catch (e: Exception) { + throw SourceException.SourceCreationFailed(e.message ?: "Unknown error") + } + } + + // CRITICAL: This method must remain available for cross-module access + // Called by PlayerModule.loadSource() + fun getSourceOrNull(nativeId: NativeId): Source? = sources[nativeId] +} + +// MARK: - Exception Definitions + +sealed class SourceException(message: String) : CodedException(message) { + class InvalidSourceConfig : SourceException("Invalid source configuration") + class SourceCreationFailed(reason: String) : SourceException("Could not create source: $reason") } diff --git a/android/src/main/java/com/bitmovin/player/reactnative/Types.kt b/android/src/main/java/com/bitmovin/player/reactnative/Types.kt new file mode 100644 index 00000000..476ed80d --- /dev/null +++ b/android/src/main/java/com/bitmovin/player/reactnative/Types.kt @@ -0,0 +1,47 @@ +package com.bitmovin.player.reactnative + +import com.bitmovin.player.api.buffer.BufferLevel +import com.bitmovin.player.api.ui.PlayerViewConfig +import com.bitmovin.player.api.ui.StyleConfig +import com.bitmovin.player.reactnative.ui.SubtitleViewConfig + +/** + * Represents the user interface type for the React Native player. + */ +enum class UserInterfaceType { + Subtitle, + Bitmovin, +} + +/** + * Configuration wrapper for Picture-in-Picture functionality. + */ +data class PictureInPictureConfig( + val isEnabled: Boolean = false, + val shouldEnterOnBackground: Boolean = false, +) + +/** + * Wrapper for React Native player view configuration. + */ +data class RNPlayerViewConfigWrapper( + val playerViewConfig: PlayerViewConfig, + val pictureInPictureConfig: PictureInPictureConfig? = null, + val subtitleViewConfig: SubtitleViewConfig?, +) + +/** + * Wrapper for React Native style configuration. + */ +data class RNStyleConfigWrapper( + val styleConfig: StyleConfig, + val userInterfaceType: UserInterfaceType, +) + +/** + * Data class for buffer levels - used for exposing buffer information. + */ +data class RNBufferLevels( + val audio: BufferLevel, + val video: BufferLevel, +) diff --git a/android/src/main/java/com/bitmovin/player/reactnative/UuidModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/UuidModule.kt deleted file mode 100644 index 5d9a5dbf..00000000 --- a/android/src/main/java/com/bitmovin/player/reactnative/UuidModule.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.bitmovin.player.reactnative - -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactMethod -import java.util.UUID - -private const val MODULE_NAME = "UuidModule" - -class UuidModule(context: ReactApplicationContext) : BitmovinBaseModule(context) { - /** - * Exported JS module name. - */ - override fun getName() = MODULE_NAME - - /** - * Synchronously generate a random UUIDv4. - * @return Random UUID RFC 4122 version 4. - */ - @ReactMethod(isBlockingSynchronousMethod = true) - fun generate() = UUID.randomUUID().toString() -} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt b/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt index a514b05f..c8311fc2 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt @@ -5,6 +5,9 @@ import com.bitmovin.analytics.api.AnalyticsConfig import com.bitmovin.analytics.api.CustomData import com.bitmovin.analytics.api.DefaultMetadata import com.bitmovin.analytics.api.SourceMetadata +import com.bitmovin.player.reactnative.extensions.get +import com.bitmovin.player.reactnative.extensions.set +import com.bitmovin.player.api.BandwidthMeterType import com.bitmovin.player.api.DeviceDescription.DeviceName import com.bitmovin.player.api.ForceReuseVideoCodecReason import com.bitmovin.player.api.PlaybackConfig @@ -24,6 +27,8 @@ import com.bitmovin.player.api.buffer.BufferLevel import com.bitmovin.player.api.buffer.BufferMediaTypeConfig import com.bitmovin.player.api.buffer.BufferType import com.bitmovin.player.api.casting.RemoteControlConfig +import com.bitmovin.player.api.decoder.DecoderPriorityProvider.DecoderContext +import com.bitmovin.player.api.decoder.MediaCodecInfo import com.bitmovin.player.api.drm.WidevineConfig import com.bitmovin.player.api.event.PlayerEvent import com.bitmovin.player.api.event.SourceEvent @@ -31,6 +36,7 @@ import com.bitmovin.player.api.event.data.CastPayload import com.bitmovin.player.api.event.data.SeekPosition import com.bitmovin.player.api.live.LiveConfig import com.bitmovin.player.api.media.AdaptationConfig +import com.bitmovin.player.api.media.MediaTrackRole import com.bitmovin.player.api.media.MediaType import com.bitmovin.player.api.media.audio.AudioTrack import com.bitmovin.player.api.media.subtitle.SubtitleTrack @@ -53,25 +59,22 @@ import com.bitmovin.player.api.ui.ScalingMode import com.bitmovin.player.api.ui.StyleConfig import com.bitmovin.player.api.ui.SurfaceType import com.bitmovin.player.api.ui.UiConfig -import com.bitmovin.player.reactnative.BitmovinCastManagerOptions import com.bitmovin.player.reactnative.PictureInPictureConfig import com.bitmovin.player.reactnative.RNBufferLevels import com.bitmovin.player.reactnative.RNPlayerViewConfigWrapper import com.bitmovin.player.reactnative.RNStyleConfigWrapper import com.bitmovin.player.reactnative.UserInterfaceType -import com.bitmovin.player.reactnative.extensions.get +import com.bitmovin.player.reactnative.extensions.getArray import com.bitmovin.player.reactnative.extensions.getBooleanOrNull import com.bitmovin.player.reactnative.extensions.getDoubleOrNull +import com.bitmovin.player.reactnative.extensions.getInt import com.bitmovin.player.reactnative.extensions.getIntOrNull +import com.bitmovin.player.reactnative.extensions.getMap import com.bitmovin.player.reactnative.extensions.getName -import com.bitmovin.player.reactnative.extensions.mapToReactArray -import com.bitmovin.player.reactnative.extensions.putBoolean -import com.bitmovin.player.reactnative.extensions.putDouble -import com.bitmovin.player.reactnative.extensions.putInt -import com.bitmovin.player.reactnative.extensions.set +import com.bitmovin.player.reactnative.extensions.getString +import com.bitmovin.player.reactnative.extensions.toBase64DataUri import com.bitmovin.player.reactnative.extensions.toMap import com.bitmovin.player.reactnative.extensions.toMapList -import com.bitmovin.player.reactnative.extensions.toReadableMap import com.bitmovin.player.reactnative.extensions.withArray import com.bitmovin.player.reactnative.extensions.withBoolean import com.bitmovin.player.reactnative.extensions.withDouble @@ -80,13 +83,15 @@ import com.bitmovin.player.reactnative.extensions.withMap import com.bitmovin.player.reactnative.extensions.withString import com.bitmovin.player.reactnative.extensions.withStringArray import com.bitmovin.player.reactnative.ui.SubtitleViewConfig -import com.facebook.react.bridge.* import java.util.UUID /** - * Converts an arbitrary `json` to `PlayerConfig`. + * Filters out null values from a map to ensure compatibility with Expo modules */ -fun ReadableMap.toPlayerConfig(): PlayerConfig = PlayerConfig(key = getString("licenseKey")).apply { +private fun Map.filterNotNullValues(): Map = + this.filterValues { it != null }.mapValues { it.value!! } + +fun Map.toPlayerConfig(): PlayerConfig = PlayerConfig(key = getString("licenseKey")).apply { withMap("playbackConfig") { playbackConfig = it.toPlaybackConfig() } withMap("styleConfig") { styleConfig = it.toStyleConfig() } withMap("tweaksConfig") { tweaksConfig = it.toTweaksConfig() } @@ -98,72 +103,48 @@ fun ReadableMap.toPlayerConfig(): PlayerConfig = PlayerConfig(key = getString("l withMap("networkConfig") { networkConfig = it.toNetworkConfig() } } -/** - * Converts any JS object into a `BufferMediaTypeConfig` object. - */ -fun ReadableMap.toBufferMediaTypeConfig(): BufferMediaTypeConfig = BufferMediaTypeConfig().apply { +fun Map.toBufferMediaTypeConfig(): BufferMediaTypeConfig = BufferMediaTypeConfig().apply { withDouble("forwardDuration") { forwardDuration = it } } -/** - * Converts any JS object into a `BufferConfig` object. - */ -fun ReadableMap.toBufferConfig(): BufferConfig = BufferConfig().apply { +fun Map.toBufferConfig(): BufferConfig = BufferConfig().apply { withMap("audioAndVideo") { audioAndVideo = it.toBufferMediaTypeConfig() } withDouble("restartThreshold") { restartThreshold = it } withDouble("startupThreshold") { startupThreshold = it } } -/** - * Converts an arbitrary [ReadableMap] to a [RemoteControlConfig]. - */ -private fun ReadableMap.toRemoteControlConfig(): RemoteControlConfig = RemoteControlConfig().apply { +private fun Map.toRemoteControlConfig(): RemoteControlConfig = RemoteControlConfig().apply { withString("receiverStylesheetUrl") { receiverStylesheetUrl = it } - withMap("customReceiverConfig") { customReceiverConfig = it.toMap() } + withMap("customReceiverConfig") { customReceiverConfig = it.mapValues { entry -> entry.value as? String } } withBoolean("isCastEnabled") { isCastEnabled = it } withBoolean("sendManifestRequestsWithCredentials") { sendManifestRequestsWithCredentials = it } withBoolean("sendSegmentRequestsWithCredentials") { sendSegmentRequestsWithCredentials = it } withBoolean("sendDrmLicenseRequestsWithCredentials") { sendDrmLicenseRequestsWithCredentials = it } } -/** - * Converts an arbitrary `json` to `SourceOptions`. - */ -fun ReadableMap.toSourceOptions(): SourceOptions = SourceOptions( +fun Map.toSourceOptions(): SourceOptions = SourceOptions( startOffset = getDoubleOrNull("startOffset"), startOffsetTimelineReference = getString("startOffsetTimelineReference")?.toTimelineReferencePoint(), ) -/** - * Converts an arbitrary `json` to `TimelineReferencePoint`. - */ private fun String.toTimelineReferencePoint(): TimelineReferencePoint? = when (this) { "start" -> TimelineReferencePoint.Start "end" -> TimelineReferencePoint.End else -> null } -/** - * Converts an arbitrary `json` to `AdaptationConfig`. - */ -private fun ReadableMap.toAdaptationConfig(): AdaptationConfig = AdaptationConfig().apply { +private fun Map.toAdaptationConfig(): AdaptationConfig = AdaptationConfig().apply { withInt("maxSelectableBitrate") { maxSelectableVideoBitrate = it } withInt("initialBandwidthEstimateOverride") { initialBandwidthEstimateOverride = it.toLong(); } } -/** - * Converts any JS object into a `PlaybackConfig` object. - */ -fun ReadableMap.toPlaybackConfig(): PlaybackConfig = PlaybackConfig().apply { +fun Map.toPlaybackConfig(): PlaybackConfig = PlaybackConfig().apply { withBoolean("isAutoplayEnabled") { isAutoplayEnabled = it } withBoolean("isMuted") { isMuted = it } withBoolean("isTimeShiftEnabled") { isTimeShiftEnabled = it } } -/** - * Converts any JS object into a `StyleConfig` object. - */ -fun ReadableMap.toStyleConfig(): StyleConfig = StyleConfig().apply { +fun Map.toStyleConfig(): StyleConfig = StyleConfig().apply { withBoolean("isUiEnabled") { isUiEnabled = it } getString("playerUiCss")?.takeIf { it.isNotEmpty() }?.let { playerUiCss = it } getString("supplementalPlayerUiCss")?.takeIf { it.isNotEmpty() }?.let { supplementalPlayerUiCss = it } @@ -171,9 +152,6 @@ fun ReadableMap.toStyleConfig(): StyleConfig = StyleConfig().apply { withString("scalingMode") { scalingMode = ScalingMode.valueOf(it) } } -/** - * Converts any JS string into an `ForceReuseVideoCodecReason` enum value. - */ private fun String.toForceReuseVideoCodecReason(): ForceReuseVideoCodecReason? = when (this) { "ColorInfoMismatch" -> ForceReuseVideoCodecReason.ColorInfoMismatch "MaxInputSizeExceeded" -> ForceReuseVideoCodecReason.MaxInputSizeExceeded @@ -181,12 +159,13 @@ private fun String.toForceReuseVideoCodecReason(): ForceReuseVideoCodecReason? = else -> null } -/** - * Converts any JS object into a `TweaksConfig` object. - */ -fun ReadableMap.toTweaksConfig(): TweaksConfig = TweaksConfig().apply { +fun Map.toTweaksConfig(): TweaksConfig = TweaksConfig().apply { withDouble("timeChangedInterval") { timeChangedInterval = it } - withInt("bandwidthEstimateWeightLimit") { bandwidthEstimateWeightLimit = it } + withInt("bandwidthEstimateWeightLimit") { + bandwidthMeterType = BandwidthMeterType.Default( + bandwidthEstimateWeightLimit = it, + ) + } withMap("devicesThatRequireSurfaceWorkaround") { devices -> val deviceNames = devices.withStringArray("deviceNames") { it.filterNotNull().map(::DeviceName) @@ -198,11 +177,9 @@ fun ReadableMap.toTweaksConfig(): TweaksConfig = TweaksConfig().apply { } withBoolean("languagePropertyNormalization") { languagePropertyNormalization = it } withDouble("localDynamicDashWindowUpdateInterval") { localDynamicDashWindowUpdateInterval = it } - withBoolean("shouldApplyTtmlRegionWorkaround") { shouldApplyTtmlRegionWorkaround = it } withBoolean("useDrmSessionForClearPeriods") { useDrmSessionForClearPeriods = it } withBoolean("useDrmSessionForClearSources") { useDrmSessionForClearSources = it } withBoolean("useFiletypeExtractorFallbackForHls") { useFiletypeExtractorFallbackForHls = it } - withBoolean("preferSoftwareDecodingForAds") { preferSoftwareDecodingForAds = it } withStringArray("forceReuseVideoCodecReasons") { forceReuseVideoCodecReasons = it .filterNotNull() @@ -211,39 +188,27 @@ fun ReadableMap.toTweaksConfig(): TweaksConfig = TweaksConfig().apply { } } -/** - * Converts any JS object into an `AdvertisingConfig` object. - */ -fun ReadableMap.toAdvertisingConfig(): AdvertisingConfig? { +fun Map.toAdvertisingConfig(): AdvertisingConfig? { return AdvertisingConfig( getArray("schedule")?.toMapList()?.mapNotNull { it?.toAdItem() } ?: return null, ) } -/** - * Converts any JS object into an `AdItem` object. - */ -fun ReadableMap.toAdItem(): AdItem? { +fun Map.toAdItem(): AdItem? { return AdItem( - sources = getArray("sources") ?.toMapList()?.mapNotNull { it?.toAdSource() }?.toTypedArray() ?: return null, + sources = getArray("sources")?.toMapList()?.mapNotNull { it?.toAdSource() }?.toTypedArray() ?: return null, position = getString("position") ?: "pre", preloadOffset = getDoubleOrNull("preloadOffset") ?: 0.0, ) } -/** - * Converts any JS object into an `AdSource` object. - */ -fun ReadableMap.toAdSource(): AdSource? { +fun Map.toAdSource(): AdSource? { return AdSource( type = getString("type")?.toAdSourceType() ?: return null, tag = getString("tag") ?: return null, ) } -/** - * Converts any JS string into an `AdSourceType` enum value. - */ private fun String.toAdSourceType(): AdSourceType? = when (this) { "bitmovin" -> AdSourceType.Bitmovin "ima" -> AdSourceType.Ima @@ -252,10 +217,7 @@ private fun String.toAdSourceType(): AdSourceType? = when (this) { else -> null } -/** - * Converts an arbitrary `json` to `SourceConfig`. - */ -fun ReadableMap.toSourceConfig(): SourceConfig? { +fun Map.toSourceConfig(): SourceConfig? { val url = getString("url") ?: return null val type = getString("type")?.toSourceType() ?: return null return SourceConfig(url, type).apply { @@ -264,8 +226,8 @@ fun ReadableMap.toSourceConfig(): SourceConfig? { withString("poster") { posterSource = it } withBoolean("isPosterPersistent") { isPosterPersistent = it } withArray("subtitleTracks") { subtitleTracks -> - for (i in 0 until subtitleTracks.size()) { - subtitleTracks.getMap(i).toSubtitleTrack()?.let { + subtitleTracks.indices.forEach { subtitleTrack -> + subtitleTracks.getMap(subtitleTrack)?.toSubtitleTrack()?.let { addSubtitleTrack(it) } } @@ -276,9 +238,6 @@ fun ReadableMap.toSourceConfig(): SourceConfig? { } } -/** - * Converts an arbitrary `json` to `SourceType`. - */ fun String.toSourceType(): SourceType? = when (this) { "dash" -> SourceType.Dash "hls" -> SourceType.Hls @@ -287,277 +246,252 @@ fun String.toSourceType(): SourceType? = when (this) { else -> null } -/** - * Converts any given `Source` object into its `json` representation. - */ -fun Source.toJson(): WritableMap = Arguments.createMap().apply { - putDouble("duration", duration) - putBoolean("isActive", isActive) - putBoolean("isAttachedToPlayer", isAttachedToPlayer) - putInt("loadingState", loadingState.ordinal) - putMap("metadata", config.metadata?.toReadableMap()) -} +fun Source.toJson(): Map = mapOf( + "duration" to duration, + "isActive" to isActive, + "isAttachedToPlayer" to isAttachedToPlayer, + "loadingState" to loadingState.ordinal, + "metadata" to (config.metadata ?: emptyMap()), +).filterNotNullValues() -/** - * Converts any given `SeekPosition` object into its `json` representation. - */ -fun SeekPosition.toJson(): WritableMap = Arguments.createMap().apply { - putDouble("time", time) - putMap("source", source.toJson()) -} +fun SeekPosition.toJson(): Map = mapOf( + "time" to time, + "source" to source.toJson(), +).filterNotNullValues() + +fun SourceEvent.toJson(): Map { + val baseMap = mutableMapOf( + "name" to getName(), + "timestamp" to timestamp.toDouble(), + ) -/** - * Converts any given `SourceEvent` object into its `json` representation. - */ -fun SourceEvent.toJson(): WritableMap { - val json = Arguments.createMap() - json.putString("name", getName()) - json.putDouble("timestamp", timestamp.toDouble()) when (this) { is SourceEvent.Load -> { - json.putMap("source", source.toJson()) + baseMap["source"] = source.toJson() } is SourceEvent.Loaded -> { - json.putMap("source", source.toJson()) + baseMap["source"] = source.toJson() } is SourceEvent.Error -> { - json.putInt("code", code.value) - json.putString("message", message) + baseMap["code"] = code.value + baseMap["message"] = message } is SourceEvent.Warning -> { - json.putInt("code", code.value) - json.putString("message", message) + baseMap["code"] = code.value + baseMap["message"] = message } is SourceEvent.AudioTrackAdded -> { - json.putMap("audioTrack", audioTrack.toJson()) + baseMap["audioTrack"] = audioTrack.toJson() } is SourceEvent.AudioTrackChanged -> { - json.putMap("oldAudioTrack", oldAudioTrack?.toJson()) - json.putMap("newAudioTrack", newAudioTrack?.toJson()) + baseMap["oldAudioTrack"] = oldAudioTrack?.toJson() + baseMap["newAudioTrack"] = newAudioTrack?.toJson() } is SourceEvent.AudioTrackRemoved -> { - json.putMap("audioTrack", audioTrack.toJson()) + baseMap["audioTrack"] = audioTrack.toJson() } is SourceEvent.SubtitleTrackAdded -> { - json.putMap("subtitleTrack", subtitleTrack.toJson()) + baseMap["subtitleTrack"] = subtitleTrack.toJson() } is SourceEvent.SubtitleTrackRemoved -> { - json.putMap("subtitleTrack", subtitleTrack.toJson()) + baseMap["subtitleTrack"] = subtitleTrack.toJson() } is SourceEvent.SubtitleTrackChanged -> { - json.putMap("oldSubtitleTrack", oldSubtitleTrack?.toJson()) - json.putMap("newSubtitleTrack", newSubtitleTrack?.toJson()) + baseMap["oldSubtitleTrack"] = oldSubtitleTrack?.toJson() + baseMap["newSubtitleTrack"] = newSubtitleTrack?.toJson() } is SourceEvent.DownloadFinished -> { - json.putDouble("downloadTime", downloadTime) - json.putString("requestType", downloadType.toString()) - json.putInt("httpStatus", httpStatus) - json.putBoolean("isSuccess", isSuccess) + baseMap["downloadTime"] = downloadTime + baseMap["requestType"] = downloadType.toString() + baseMap["httpStatus"] = httpStatus + baseMap["isSuccess"] = isSuccess lastRedirectLocation?.let { - json.putString("lastRedirectLocation", it) + baseMap["lastRedirectLocation"] = it } - json.putDouble("size", size.toDouble()) - json.putString("url", url) + baseMap["size"] = size.toDouble() + baseMap["url"] = url } is SourceEvent.VideoDownloadQualityChanged -> { - json.putMap("newVideoQuality", newVideoQuality?.toJson()) - json.putMap("oldVideoQuality", oldVideoQuality?.toJson()) + baseMap["newVideoQuality"] = newVideoQuality?.toJson() + baseMap["oldVideoQuality"] = oldVideoQuality?.toJson() } else -> { // Event is not supported yet or does not have any additional data } } - return json + return baseMap.filterNotNullValues() } -/** - * Converts any given `PlayerEvent` object into its `json` representation. - */ -fun PlayerEvent.toJson(): WritableMap { - val json = Arguments.createMap() - json.putString("name", getName()) - json.putDouble("timestamp", timestamp.toDouble()) +fun PlayerEvent.toJson(): Map { + val baseMap = mutableMapOf( + "name" to getName(), + "timestamp" to timestamp.toDouble(), + ) + when (this) { is PlayerEvent.Error -> { - json.putInt("code", code.value) - json.putString("message", message) + baseMap["code"] = code.value + baseMap["message"] = message } is PlayerEvent.Warning -> { - json.putInt("code", code.value) - json.putString("message", message) + baseMap["code"] = code.value + baseMap["message"] = message } is PlayerEvent.Play -> { - json.putDouble("time", time) + baseMap["time"] = time } is PlayerEvent.Playing -> { - json.putDouble("time", time) + baseMap["time"] = time } is PlayerEvent.Paused -> { - json.putDouble("time", time) + baseMap["time"] = time } is PlayerEvent.TimeChanged -> { - json.putDouble("currentTime", time) + baseMap["currentTime"] = time } is PlayerEvent.Seek -> { - json.putMap("from", from.toJson()) - json.putMap("to", to.toJson()) + baseMap["from"] = from.toJson() + baseMap["to"] = to.toJson() } is PlayerEvent.TimeShift -> { - json.putDouble("position", position) - json.putDouble("targetPosition", target) + baseMap["position"] = position + baseMap["targetPosition"] = target } is PlayerEvent.PictureInPictureAvailabilityChanged -> { - json.putBoolean("isPictureInPictureAvailable", isPictureInPictureAvailable) + baseMap["isPictureInPictureAvailable"] = isPictureInPictureAvailable } is PlayerEvent.AdBreakFinished -> { - json.putMap("adBreak", adBreak?.toJson()) + baseMap["adBreak"] = adBreak?.toJson() } is PlayerEvent.AdBreakStarted -> { - json.putMap("adBreak", adBreak?.toJson()) + baseMap["adBreak"] = adBreak?.toJson() } is PlayerEvent.AdClicked -> { - json.putString("clickThroughUrl", clickThroughUrl) + baseMap["clickThroughUrl"] = clickThroughUrl } is PlayerEvent.AdError -> { - json.putInt("code", code) - json.putString("message", message) - json.putMap("adConfig", adConfig?.toJson()) - json.putMap("adItem", adItem?.toJson()) + baseMap["code"] = code + baseMap["message"] = message + baseMap["adConfig"] = adConfig?.toJson() + baseMap["adItem"] = adItem?.toJson() } is PlayerEvent.AdFinished -> { - json.putMap("ad", ad?.toJson()) + baseMap["ad"] = ad?.toJson() } is PlayerEvent.AdManifestLoad -> { - json.putMap("adBreak", adBreak?.toJson()) - json.putMap("adConfig", adConfig.toJson()) + baseMap["adBreak"] = adBreak?.toJson() + baseMap["adConfig"] = adConfig.toJson() } is PlayerEvent.AdManifestLoaded -> { - json.putMap("adBreak", adBreak?.toJson()) - json.putMap("adConfig", adConfig.toJson()) - json.putDouble("downloadTime", downloadTime.toDouble()) + baseMap["adBreak"] = adBreak?.toJson() + baseMap["adConfig"] = adConfig.toJson() + baseMap["downloadTime"] = downloadTime.toDouble() } is PlayerEvent.AdQuartile -> { - json.putString("quartile", quartile.toJson()) + baseMap["quartile"] = quartile.toJson() } is PlayerEvent.AdScheduled -> { - json.putInt("numberOfAds", numberOfAds) + baseMap["numberOfAds"] = numberOfAds } is PlayerEvent.AdSkipped -> { - json.putMap("ad", ad?.toJson()) + baseMap["ad"] = ad?.toJson() } is PlayerEvent.AdStarted -> { - json.putMap("ad", ad?.toJson()) - json.putString("clickThroughUrl", clickThroughUrl) - json.putString("clientType", clientType?.toJson()) - json.putDouble("duration", duration) - json.putInt("indexInQueue", indexInQueue) - json.putString("position", position) - json.putDouble("skipOffset", skipOffset) - json.putDouble("timeOffset", timeOffset) + baseMap["ad"] = ad?.toJson() + baseMap["clickThroughUrl"] = clickThroughUrl + baseMap["clientType"] = clientType?.toJson() + baseMap["duration"] = duration + baseMap["indexInQueue"] = indexInQueue + baseMap["position"] = position + baseMap["skipOffset"] = skipOffset + baseMap["timeOffset"] = timeOffset } is PlayerEvent.VideoPlaybackQualityChanged -> { - json.putMap("newVideoQuality", newVideoQuality?.toJson()) - json.putMap("oldVideoQuality", oldVideoQuality?.toJson()) + baseMap["newVideoQuality"] = newVideoQuality?.toJson() + baseMap["oldVideoQuality"] = oldVideoQuality?.toJson() } is PlayerEvent.CastWaitingForDevice -> { - json.putMap("castPayload", castPayload.toJson()) + baseMap["castPayload"] = castPayload.toJson() } is PlayerEvent.CastStarted -> { - json.putString("deviceName", deviceName) + baseMap["deviceName"] = deviceName } is PlayerEvent.CueEnter -> { - json.putDouble("start", start) - json.putDouble("end", end) - json.putString("text", text) + baseMap["start"] = start + baseMap["end"] = end + baseMap["text"] = text + baseMap["image"] = image?.toBase64DataUri() } is PlayerEvent.CueExit -> { - json.putDouble("start", start) - json.putDouble("end", end) - json.putString("text", text) + baseMap["start"] = start + baseMap["end"] = end + baseMap["text"] = text + baseMap["image"] = image?.toBase64DataUri() } else -> { // Event is not supported yet or does not have any additional data } } - return json + return baseMap.filterNotNullValues() } -/** - * Converts an arbitrary `json` into [BitmovinCastManagerOptions]. - */ -fun ReadableMap.toCastOptions(): BitmovinCastManagerOptions = BitmovinCastManagerOptions( - applicationId = getString("applicationId"), - messageNamespace = getString("messageNamespace"), -) - -/** - * Converts an arbitrary `json` to `WidevineConfig`. - */ -fun ReadableMap.toWidevineConfig(): WidevineConfig? = getMap("widevine")?.run { +fun Map.toWidevineConfig(): WidevineConfig? = getMap("widevine")?.run { WidevineConfig(getString("licenseUrl")).apply { withString("preferredSecurityLevel") { preferredSecurityLevel = it } withBoolean("shouldKeepDrmSessionsAlive") { shouldKeepDrmSessionsAlive = it } - withMap("httpHeaders") { httpHeaders = it.toMap().toMutableMap() } + withMap("httpHeaders") { httpHeaders = it.mapValues { entry -> entry.value as String }.toMutableMap() } } } -/** - * Converts an `url` string into a `ThumbnailsTrack`. - */ fun String.toThumbnailTrack(): ThumbnailTrack = ThumbnailTrack(this) -/** - * Converts any `AudioTrack` into its json representation. - */ -fun AudioTrack.toJson(): WritableMap = Arguments.createMap().apply { - putString("url", url) - putString("label", label) - putBoolean("isDefault", isDefault) - putString("identifier", id) - putString("language", language) -} +fun AudioTrack.toJson(): Map = mapOf( + "url" to url, + "label" to label, + "isDefault" to isDefault, + "identifier" to id, + "language" to language, + "roles" to roles.map { it.toJson() }, +).filterNotNullValues() -/** - * Converts an arbitrary `json` into a `SubtitleTrack`. - */ -fun ReadableMap.toSubtitleTrack(): SubtitleTrack? { +fun Map.toSubtitleTrack(): SubtitleTrack? { return SubtitleTrack( url = getString("url") ?: return null, label = getString("label") ?: return null, @@ -569,91 +503,62 @@ fun ReadableMap.toSubtitleTrack(): SubtitleTrack? { ) } -/** - * Converts any subtitle format name in its mime type representation. - */ private fun String.toSubtitleMimeType(): String = when (this) { "srt" -> "application/x-subrip" "ttml" -> "application/ttml+xml" else -> "text/$this" } -/** - * Converts any `SubtitleTrack` into its json representation. - */ -fun SubtitleTrack.toJson(): WritableMap = Arguments.createMap().apply { - putString("url", url) - putString("label", label) - putBoolean("isDefault", isDefault) - putString("identifier", id) - putString("language", language) - putBoolean("isForced", isForced) - putString("format", mimeType?.textMimeTypeToJson()) -} +fun SubtitleTrack.toJson(): Map = mapOf( + "url" to url, + "label" to label, + "isDefault" to isDefault, + "identifier" to id, + "language" to language, + "isForced" to isForced, + "format" to mimeType?.textMimeTypeToJson(), + "roles" to roles.map { it.toJson() }, +).filterNotNullValues() -/** - * Converts any subtitle track mime type into its json representation (file format value). - */ private fun String.textMimeTypeToJson(): String = split("/").last() -/** - * Converts any `AdBreak` object into its json representation. - */ -fun AdBreak.toJson(): WritableMap = Arguments.createMap().apply { - putArray("ads", ads.mapToReactArray { it.toJson() }) - putString("id", id) - putDouble("scheduleTime", scheduleTime) -} - -/** - * Converts any `Ad` object into its json representation. - */ -fun Ad.toJson(): WritableMap = Arguments.createMap().apply { - putString("clickThroughUrl", clickThroughUrl) - putMap("data", data?.toJson()) - putInt("height", height) - putString("id", id) - putBoolean("isLinear", isLinear) - putString("mediaFileUrl", mediaFileUrl) - putInt("width", width) -} - -/** - * Converts any `AdData` object into its json representation. - */ -fun AdData.toJson(): WritableMap = Arguments.createMap().apply { - putInt("bitrate", bitrate) - putInt("maxBitrate", maxBitrate) - putString("mimeType", mimeType) - putInt("minBitrate", minBitrate) -} - -/** - * Converts any `AdConfig` object into its json representation. - */ -fun AdConfig.toJson(): WritableMap = Arguments.createMap().apply { - putDouble("replaceContentDuration", replaceContentDuration) -} +fun AdBreak.toJson(): Map = mapOf( + "ads" to ads.map { it.toJson() }, + "id" to id, + "scheduleTime" to scheduleTime, +) -/** - * Converts any `AdItem` object into its json representation. - */ -fun AdItem.toJson(): WritableMap = Arguments.createMap().apply { - putString("position", position) - putArray("sources", sources.toList().mapToReactArray { it.toJson() }) -} +fun Ad.toJson(): Map = mapOf( + "clickThroughUrl" to clickThroughUrl, + "data" to data?.toJson(), + "height" to height, + "id" to id, + "isLinear" to isLinear, + "mediaFileUrl" to mediaFileUrl, + "width" to width, +).filterNotNullValues() + +fun AdData.toJson(): Map = mapOf( + "bitrate" to bitrate, + "maxBitrate" to maxBitrate, + "mimeType" to mimeType, + "minBitrate" to minBitrate, +).filterNotNullValues() + +fun AdConfig.toJson(): Map = mapOf( + "replaceContentDuration" to replaceContentDuration, +).filterNotNullValues() + +fun AdItem.toJson(): Map = mapOf( + "position" to position, + "sources" to sources.toList().map { it.toJson() }, +) -/** - * Converts any `AdSource` object into its json representation. - */ -fun AdSource.toJson(): WritableMap = Arguments.createMap().apply { - putString("tag", tag) - putString("type", type.toJson()) -} +fun AdSource.toJson(): Map = mapOf( + "tag" to tag, + "type" to type.toJson(), +) -/** - * Converts any `AdSourceType` value into its json representation. - */ fun AdSourceType.toJson(): String = when (this) { AdSourceType.Bitmovin -> "bitmovin" AdSourceType.Ima -> "ima" @@ -661,38 +566,26 @@ fun AdSourceType.toJson(): String = when (this) { AdSourceType.Progressive -> "progressive" } -/** - * Converts any `AdQuartile` value into its json representation. - */ fun AdQuartile.toJson(): String = when (this) { AdQuartile.FirstQuartile -> "first" AdQuartile.MidPoint -> "mid_point" AdQuartile.ThirdQuartile -> "third" } -/** - * Converts an arbitrary json object into a `BitmovinAnalyticsConfig`. - */ -fun ReadableMap.toAnalyticsConfig(): AnalyticsConfig? = getString("licenseKey") +fun Map.toAnalyticsConfig(): AnalyticsConfig? = getString("licenseKey") ?.let { AnalyticsConfig.Builder(it) } ?.apply { withBoolean("adTrackingDisabled") { setAdTrackingDisabled(it) } withBoolean("randomizeUserId") { setRandomizeUserId(it) } }?.build() -/** - * Converts an arbitrary json object into an analytics `DefaultMetadata`. - */ -fun ReadableMap.toAnalyticsDefaultMetadata(): DefaultMetadata = DefaultMetadata.Builder().apply { +fun Map.toAnalyticsDefaultMetadata(): DefaultMetadata = DefaultMetadata.Builder().apply { setCustomData(toAnalyticsCustomData()) withString("cdnProvider") { setCdnProvider(it) } withString("customUserId") { setCustomUserId(it) } }.build() -/** - * Converts an arbitrary json object into an analytics `CustomData`. - */ -fun ReadableMap.toAnalyticsCustomData(): CustomData = CustomData.Builder().apply { +fun Map.toAnalyticsCustomData(): CustomData = CustomData.Builder().apply { for (n in 1..30) { this[n] = getString("customData$n") } @@ -701,17 +594,16 @@ fun ReadableMap.toAnalyticsCustomData(): CustomData = CustomData.Builder().apply } }.build() -/** - * Converts an arbitrary analytics `CustomData` object into a JS value. - */ -fun CustomData.toJson(): WritableMap = Arguments.createMap().also { json -> +fun CustomData.toJson(): Map { + val map = mutableMapOf() for (n in 1..30) { - json.putStringIfNotNull("customData$n", this[n]) + this[n]?.let { map["customData$n"] = it } } - json.putStringIfNotNull("experimentName", experimentName) + experimentName?.let { map["experimentName"] = it } + return map.filterNotNullValues() } -fun ReadableMap.toAnalyticsSourceMetadata(): SourceMetadata = SourceMetadata( +fun Map.toAnalyticsSourceMetadata(): SourceMetadata = SourceMetadata( title = getString("title"), videoId = getString("videoId"), cdnProvider = getString("cdnProvider"), @@ -720,67 +612,60 @@ fun ReadableMap.toAnalyticsSourceMetadata(): SourceMetadata = SourceMetadata( customData = toAnalyticsCustomData(), ) -fun SourceMetadata.toJson(): ReadableMap = customData.toJson().also { - it.putString("title", title) - it.putString("videoId", videoId) - it.putString("cdnProvider", cdnProvider) - it.putString("path", path) - it.putBoolean("isLive", isLive) -} - -/** - * Converts any `VideoQuality` value into its json representation. - */ -fun VideoQuality.toJson(): WritableMap = Arguments.createMap().apply { - putString("id", id) - putString("label", label) - putInt("bitrate", bitrate) - putString("codec", codec) - putDouble("frameRate", frameRate.toDouble()) - putInt("height", height) - putInt("width", width) -} - -/** - * Converts any `OfflineOptionEntry` into its json representation. - */ -fun OfflineOptionEntry.toJson(): WritableMap = Arguments.createMap().apply { - putString("id", id) - putString("language", language) -} - -/** - * Converts any `OfflineContentOptions` into its json representation. - */ -fun OfflineContentOptions.toJson(): WritableMap = Arguments.createMap().apply { - putArray("audioOptions", audioOptions.mapToReactArray { it.toJson() }) - putArray("textOptions", textOptions.mapToReactArray { it.toJson() }) -} +fun SourceMetadata.toJson(): Map { + val map = customData.toJson().toMutableMap() + map["title"] = title + map["videoId"] = videoId + map["cdnProvider"] = cdnProvider + map["path"] = path + map["isLive"] = isLive + return map.filterNotNullValues() +} + +fun VideoQuality.toJson(): Map = mapOf( + "id" to id, + "label" to label, + "bitrate" to bitrate, + "codec" to codec, + "frameRate" to frameRate.toDouble(), + "height" to height, + "width" to width, +).filterNotNullValues() + +fun OfflineOptionEntry.toJson(): Map = mapOf( + "id" to id, + "language" to language, +).filterNotNullValues() + +fun OfflineContentOptions.toJson(): Map = mapOf( + "audioOptions" to audioOptions.map { it.toJson() }, + "textOptions" to textOptions.map { it.toJson() }, +) -fun Thumbnail.toJson(): WritableMap = Arguments.createMap().apply { - putDouble("start", start) - putDouble("end", end) - putString("text", text) - putString("url", uri.toString()) - putInt("x", x) - putInt("y", y) - putInt("width", width) - putInt("height", height) -} +fun Thumbnail.toJson(): Map = mapOf( + "start" to start, + "end" to end, + "text" to text, + "url" to uri.toString(), + "x" to x, + "y" to y, + "width" to width, + "height" to height, +) -fun ReadableMap.toPictureInPictureConfig(): PictureInPictureConfig = PictureInPictureConfig( +fun Map.toPictureInPictureConfig(): PictureInPictureConfig = PictureInPictureConfig( isEnabled = getBooleanOrNull("isEnabled") ?: false, shouldEnterOnBackground = getBooleanOrNull("shouldEnterOnBackground") ?: false, ) -fun ReadableMap.toSubtitleViewConfig(): SubtitleViewConfig = SubtitleViewConfig( +fun Map.toSubtitleViewConfig(): SubtitleViewConfig = SubtitleViewConfig( paddingLeft = getIntOrNull("paddingLeft") ?: 0, paddingTop = getIntOrNull("paddingTop") ?: 0, paddingRight = getIntOrNull("paddingRight") ?: 0, paddingBottom = getIntOrNull("paddingBottom") ?: 0, ) -fun ReadableMap.toPlayerViewConfig(): PlayerViewConfig = PlayerViewConfig( +fun Map.toPlayerViewConfig(): PlayerViewConfig = PlayerViewConfig( uiConfig = getMap("uiConfig")?.toUiConfig() ?: UiConfig.WebUi(), hideFirstFrame = getBooleanOrNull("hideFirstFrame") ?: false, surfaceType = getString("surfaceType")?.toSurfaceType() ?: SurfaceType.SurfaceView, @@ -792,7 +677,7 @@ private fun String.toSurfaceType(): SurfaceType? = when (this) { else -> null } -private fun ReadableMap.toUiConfig(): UiConfig { +private fun Map.toUiConfig(): UiConfig { val variant = toVariant() ?: UiConfig.WebUi.Variant.SmallScreenUi val focusUiOnInitialization = getBooleanOrNull("focusUiOnInitialization") val defaultFocusUiOnInitialization = variant == UiConfig.WebUi.Variant.TvUi @@ -804,7 +689,7 @@ private fun ReadableMap.toUiConfig(): UiConfig { ) } -private fun ReadableMap.toVariant(): UiConfig.WebUi.Variant? { +private fun Map.toVariant(): UiConfig.WebUi.Variant? { val uiManagerFactoryFunction = getMap("variant")?.getString("uiManagerFactoryFunction") ?: return null return when (uiManagerFactoryFunction) { @@ -814,37 +699,37 @@ private fun ReadableMap.toVariant(): UiConfig.WebUi.Variant? { } } -private fun ReadableMap.toUserInterfaceTypeFromPlayerConfig(): UserInterfaceType? = +private fun Map.toUserInterfaceTypeFromPlayerConfig(): UserInterfaceType? = when (getMap("styleConfig")?.getString("userInterfaceType")) { "Subtitle" -> UserInterfaceType.Subtitle "Bitmovin" -> UserInterfaceType.Bitmovin else -> null } -/** - * Converts the [this@toRNPlayerViewConfigWrapper] to a `RNPlayerViewConfig` object. - */ -fun ReadableMap.toRNPlayerViewConfigWrapper() = RNPlayerViewConfigWrapper( +fun String.toUserInterfaceType(): UserInterfaceType? = when (this) { + "Subtitle" -> UserInterfaceType.Subtitle + "Bitmovin" -> UserInterfaceType.Bitmovin + else -> null +} + +fun Map.toRNPlayerViewConfigWrapper() = RNPlayerViewConfigWrapper( playerViewConfig = toPlayerViewConfig(), pictureInPictureConfig = getMap("pictureInPictureConfig")?.toPictureInPictureConfig(), subtitleViewConfig = getMap("subtitleViewConfig")?.toSubtitleViewConfig(), ) -fun ReadableMap.toRNStyleConfigWrapperFromPlayerConfig(): RNStyleConfigWrapper? { +fun Map.toRNStyleConfigWrapperFromPlayerConfig(): RNStyleConfigWrapper? { return RNStyleConfigWrapper( styleConfig = toStyleConfig(), userInterfaceType = toUserInterfaceTypeFromPlayerConfig() ?: return null, ) } -/** - * Converts any JS object into a [LiveConfig] object. - */ -fun ReadableMap.toLiveConfig(): LiveConfig = LiveConfig().apply { +fun Map.toLiveConfig(): LiveConfig = LiveConfig().apply { withDouble("minTimeshiftBufferDepth") { minTimeShiftBufferDepth = it } } -fun ReadableMap.toHttpRequest(): HttpRequest? { +fun Map.toHttpRequest(): HttpRequest? { return HttpRequest( getString("url") ?: return null, getMap("headers")?.toMap(), @@ -859,7 +744,7 @@ private fun ByteArray.toBase64String(): String { private fun String.toByteArrayFromBase64(): ByteArray = Base64.decode(this, Base64.NO_WRAP) -fun ReadableMap.toHttpResponse(): HttpResponse? { +fun Map.toHttpResponse(): HttpResponse? { return HttpResponse( httpRequest = getMap("request")?.toHttpRequest() ?: return null, url = getString("url") ?: return null, @@ -869,65 +754,54 @@ fun ReadableMap.toHttpResponse(): HttpResponse? { ) } -fun ReadableMap.toNetworkConfig(): NetworkConfig = NetworkConfig() +fun Map.toNetworkConfig(): NetworkConfig = NetworkConfig() -fun HttpRequest.toJson(): WritableMap = Arguments.createMap().apply { - putString("url", url) - putMap("headers", headers?.toReadableMap()) - putString("body", body?.toBase64String()) - putString("method", method) -} +fun HttpRequest.toJson(): Map = mapOf( + "url" to url, + "headers" to headers, + "body" to body?.toBase64String(), + "method" to method, +).filterNotNullValues() -fun HttpResponse.toJson(): WritableMap = Arguments.createMap().apply { - putMap("request", httpRequest.toJson()) - putString("url", url) - putInt("status", status) - putMap("headers", headers.toReadableMap()) - putString("body", body.toBase64String()) -} +fun HttpResponse.toJson(): Map = mapOf( + "request" to httpRequest.toJson(), + "url" to url, + "status" to status, + "headers" to headers, + "body" to body.toBase64String(), +) fun HttpRequestType.toJson(): String = toString() -/** - * Converts any [MediaType] value into its json representation. - */ fun MediaType.toJson(): String = when (this) { MediaType.Audio -> "audio" MediaType.Video -> "video" } -/** - * Converts any [BufferType] value into its json representation. - */ fun BufferType.toJson(): String = when (this) { BufferType.ForwardDuration -> "forwardDuration" BufferType.BackwardDuration -> "backwardDuration" } -fun BufferLevel.toJson(): WritableMap = Arguments.createMap().apply { - putDouble("level", level) - putDouble("targetLevel", targetLevel) - putString("media", media.toJson()) - putString("type", type.toJson()) -} +fun BufferLevel.toJson(): Map = mapOf( + "level" to level, + "targetLevel" to targetLevel, + "media" to media.toJson(), + "type" to type.toJson(), +) -fun RNBufferLevels.toJson(): WritableMap = Arguments.createMap().apply { - putMap("audio", audio.toJson()) - putMap("video", video.toJson()) -} +fun RNBufferLevels.toJson(): Map = mapOf( + "audio" to audio.toJson(), + "video" to video.toJson(), +) -/** - * Maps a JS string into the corresponding [BufferType] value. - */ -fun String.toBufferType(): BufferType? = when (this) { - "forwardDuration" -> BufferType.ForwardDuration - "backwardDuration" -> BufferType.BackwardDuration - else -> null +// Extension function to convert string to BufferType +fun String.toBufferTypeOrThrow(): BufferType = when (this.lowercase()) { + "forwardduration" -> BufferType.ForwardDuration + "backwardduration" -> BufferType.BackwardDuration + else -> throw IllegalArgumentException("Unknown buffer type: $this") } -/** - * Maps a JS string into the corresponding [MediaType] value. - */ fun String.toMediaType(): MediaType? = when (this) { "audio" -> MediaType.Audio "video" -> MediaType.Video @@ -938,17 +812,48 @@ data class MediaControlConfig( var isEnabled: Boolean = true, ) -fun ReadableMap.toMediaControlConfig(): MediaControlConfig = MediaControlConfig().apply { +fun Map.toMediaControlConfig(): MediaControlConfig = MediaControlConfig().apply { withBoolean("isEnabled") { isEnabled = it } } -/** - * Converts a [CastPayload] object into its JS representation. - */ -private fun CastPayload.toJson(): WritableMap = Arguments.createMap().apply { - putDouble("currentTime", currentTime) - putString("deviceName", deviceName) - putString("type", type) +private fun CastPayload.toJson(): Map = mapOf( + "currentTime" to currentTime, + "deviceName" to deviceName, + "type" to type, +).filterNotNullValues() + +fun DecoderContext.toJson(): Map = mapOf( + "mediaType" to mediaType.name, + "isAd" to isAd, +) + +fun List.toJson(): List> = map { it.toJson() } + +fun MediaCodecInfo.toJson(): Map = mapOf( + "name" to name, + "isSoftware" to isSoftware, +) + +fun List.toMediaCodecInfoList(): List { + if (isEmpty()) { + return emptyList() + } + val mediaCodecInfoList = mutableListOf() + indices.forEach { + val info = getMap(it)?.toMediaCodecInfo() ?: return@forEach + mediaCodecInfoList.add(info) + } + return mediaCodecInfoList +} + +fun Map.toMediaCodecInfo(): MediaCodecInfo? { + val name = getString("name") ?: return null + val isSoftware = getBooleanOrNull("isSoftware") ?: return null + return MediaCodecInfo(name, isSoftware) } -private fun WritableMap.putStringIfNotNull(name: String, value: String?) = value?.let { putString(name, value) } +fun MediaTrackRole.toJson(): Map = mapOf( + "id" to id, + "schemeIdUri" to schemeIdUri, + "value" to value, +).filterNotNullValues() diff --git a/android/src/main/java/com/bitmovin/player/reactnative/extensions/Bitmap.kt b/android/src/main/java/com/bitmovin/player/reactnative/extensions/Bitmap.kt new file mode 100644 index 00000000..9bdecf12 --- /dev/null +++ b/android/src/main/java/com/bitmovin/player/reactnative/extensions/Bitmap.kt @@ -0,0 +1,12 @@ +package com.bitmovin.player.reactnative.extensions + +import android.graphics.Bitmap +import android.util.Base64 +import java.io.ByteArrayOutputStream + +fun Bitmap.toBase64DataUri(): String { + val byteArrayOutputStream = ByteArrayOutputStream() + this.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream) + val byteArray = byteArrayOutputStream.toByteArray() + return "data:image/png;base64," + Base64.encodeToString(byteArray, Base64.NO_WRAP) +} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/extensions/ListExtension.kt b/android/src/main/java/com/bitmovin/player/reactnative/extensions/ListExtension.kt new file mode 100644 index 00000000..7fc3b5fc --- /dev/null +++ b/android/src/main/java/com/bitmovin/player/reactnative/extensions/ListExtension.kt @@ -0,0 +1,89 @@ +package com.bitmovin.player.reactnative.extensions + +fun List.getBooleanOrNull(index: Int): Boolean? = + if (index in indices) get(index) as? Boolean else null + +fun List.getIntOrNull(index: Int): Int? = + if (index in indices) (get(index) as? Number)?.toInt() else null + +fun List.getInt(index: Int): Int = + if (index in indices) (get(index) as? Number)?.toInt() ?: 0 else 0 + +fun List.getDoubleOrNull(index: Int): Double? = + if (index in indices) (get(index) as? Number)?.toDouble() else null + +fun List.getString(index: Int): String? = + if (index in indices) get(index) as? String else null + +fun List.getMap(index: Int): Map? = + if (index in indices) get(index) as? Map else null + +fun List.getArray(index: Int): List? = + if (index in indices) get(index) as? List else null + +inline fun List.withDouble( + index: Int, + block: (Double) -> T, +): T? { + val value = if (index in indices) (get(index) as? Number)?.toDouble() else null + return if (value != null) block(value) else null +} + +inline fun List.withMap( + index: Int, + block: (Map) -> T, +): T? { + val value = if (index in indices) get(index) as? Map else null + return if (value != null) block(value) else null +} + +inline fun List.withInt( + index: Int, + block: (Int) -> T, +): T? { + val value = if (index in indices) (get(index) as? Number)?.toInt() else null + return if (value != null) block(value) else null +} + +inline fun List.withBoolean( + index: Int, + block: (Boolean) -> T, +): T? { + val value = if (index in indices) get(index) as? Boolean else null + return if (value != null) block(value) else null +} + +inline fun List.withString( + index: Int, + block: (String) -> T, +): T? { + val value = if (index in indices) get(index) as? String else null + return if (value != null) block(value) else null +} + +inline fun List.withArray( + index: Int, + block: (List) -> T, +): T? { + val value = if (index in indices) get(index) as? List else null + return if (value != null) block(value) else null +} + +inline fun List.withStringArray( + index: Int, + block: (List) -> T, +): T? { + val value = if (index in indices) (get(index) as? List<*>)?.map { item -> item as? String } else null + return if (value != null) block(value) else null +} + +fun List.getStringArray(index: Int): List? = + if (index in indices) (get(index) as? List<*>)?.map { it as? String } else null + +inline fun List.mapValue( + index: Int, + transform: (Any?) -> R?, +): R? = if (index in indices) transform(get(index)) else null + +inline fun List.getTyped(index: Int): T? = + if (index in indices) get(index) as? T else null \ No newline at end of file diff --git a/android/src/main/java/com/bitmovin/player/reactnative/extensions/MapExtension.kt b/android/src/main/java/com/bitmovin/player/reactnative/extensions/MapExtension.kt new file mode 100644 index 00000000..a6646da0 --- /dev/null +++ b/android/src/main/java/com/bitmovin/player/reactnative/extensions/MapExtension.kt @@ -0,0 +1,95 @@ +package com.bitmovin.player.reactnative.extensions + +fun Map.getBooleanOrNull(key: String): Boolean? = get(key) as? Boolean +fun Map.getIntOrNull(key: String): Int? = (get(key) as? Number)?.toInt() +fun Map.getInt(key: String): Int = (get(key) as? Number)?.toInt() ?: 0 +fun Map.getDoubleOrNull(key: String): Double? = (get(key) as? Number)?.toDouble() +fun Map.getString(key: String): String? = get(key) as? String +fun Map.getMap(key: String): Map? = get(key) as? Map +fun Map.getArray(key: String): List? = get(key) as? List + +inline fun Map.withDouble( + key: String, + block: (Double) -> T, +): T? { + val value = (get(key) as? Number)?.toDouble() + return if (value != null) block(value) else null +} + +inline fun Map.withMap( + key: String, + block: (Map) -> T, +): T? { + val value = get(key) as? Map + return if (value != null) block(value) else null +} + +inline fun Map.withInt( + key: String, + block: (Int) -> T, +): T? { + val value = (get(key) as? Number)?.toInt() + return if (value != null) block(value) else null +} + +inline fun Map.withBoolean( + key: String, + block: (Boolean) -> T, +): T? { + val value = get(key) as? Boolean + return if (value != null) block(value) else null +} + +inline fun Map.withString( + key: String, + block: (String) -> T, +): T? { + val value = get(key) as? String + return if (value != null) block(value) else null +} + +inline fun Map.withArray( + key: String, + block: (List) -> T, +): T? { + val value = get(key) as? List + return if (value != null) block(value) else null +} + +inline fun Map.withStringArray( + key: String, + block: (List) -> T, +): T? { + val value = (get(key) as? List<*>)?.map { item -> item as? String } + return if (value != null) block(value) else null +} + +fun Map.getStringArray(key: String): List? = (get(key) as? List<*>)?.map { it as? String } + +inline fun Map.mapValue( + key: String, + transform: (Any?) -> R?, +): R? = if (containsKey(key)) transform(get(key)) else null + +inline fun Map.toMap(): Map = mapValues { it.value as T } + +/** Convert a [Map] to [Map], adding each [T] value using [put]. */ +private inline fun Map.toMap( + put: MutableMap.(String, T) -> Unit = { key, value -> this[key] = value }, +): Map = mutableMapOf().apply { + forEach { put(it.key, it.value) } +} + +@JvmName("toStringMap") +fun Map.toMap(): Map = toMap() + +fun List.toMapList(): List?> = map { it as? Map } + +fun List.toStringList(): List = map { it as? String } +fun List.toBooleanList(): List = map { it as? Boolean } +fun List.toDoubleList(): List = map { (it as? Number)?.toDouble() } +fun List.toIntList(): List = map { (it as? Number)?.toInt() } + +inline fun List.mapToList( + transform: (T) -> Map, +): List> = map(transform) diff --git a/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReactContextExtension.kt b/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReactContextExtension.kt deleted file mode 100644 index 33158e0c..00000000 --- a/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReactContextExtension.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.bitmovin.player.reactnative.extensions - -import com.bitmovin.player.reactnative.DrmModule -import com.bitmovin.player.reactnative.NetworkModule -import com.bitmovin.player.reactnative.OfflineModule -import com.bitmovin.player.reactnative.PlayerModule -import com.bitmovin.player.reactnative.SourceModule -import com.bitmovin.player.reactnative.ui.CustomMessageHandlerModule -import com.facebook.react.bridge.* -import com.facebook.react.uimanager.UIManagerModule - -inline fun ReactContext.getModule(): T? { - return getNativeModule(T::class.java) -} - -val ReactContext.playerModule get() = getModule() -val ReactContext.sourceModule get() = getModule() -val ReactContext.offlineModule get() = getModule() -val ReactContext.uiManagerModule get() = getModule() -val ReactContext.drmModule get() = getModule() -val ReactContext.customMessageHandlerModule get() = getModule() -val ReactContext.networkModule get() = getModule() diff --git a/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReadableArray.kt b/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReadableArray.kt deleted file mode 100644 index 8b3ee30e..00000000 --- a/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReadableArray.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.bitmovin.player.reactnative.extensions - -import com.facebook.react.bridge.* - -inline fun ReadableArray.toList(convert: (Dynamic) -> T): List = (0 until size()).map { i -> - convert(getDynamic(i)) -} - -fun ReadableArray.toBooleanList() = toList { it.asBoolean() } -fun ReadableArray.toStringList() = toList { it.asString() } -fun ReadableArray.toDoubleList() = toList { it.asDouble() } -fun ReadableArray.toIntList() = toList { it.asInt() } -fun ReadableArray.toListOfArrays() = toList { it.asArray() } -fun ReadableArray.toMapList() = toList { it.asMap() } - -inline fun List.mapToReactArray( - transform: (T) -> WritableMap, -): WritableArray = Arguments.createArray().also { - forEach { element -> it.pushMap(transform(element)) } -} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReadableMap.kt b/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReadableMap.kt deleted file mode 100644 index c248521f..00000000 --- a/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReadableMap.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.bitmovin.player.reactnative.extensions - -import com.facebook.react.bridge.* - -/** Convert a [Map] to [ReadableMap], adding each [T] value using [put]. */ -private inline fun Map.toReadableMap( - put: WritableMap.(String, T) -> Unit, -): ReadableMap = Arguments.createMap().apply { - forEach { - put(it.key, it.value) - } -} - -@JvmName("toReadableStringMap") -fun Map.toReadableMap(): ReadableMap = toReadableMap(WritableMap::putString) diff --git a/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReadableMapExtension.kt b/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReadableMapExtension.kt deleted file mode 100644 index 7668aeca..00000000 --- a/android/src/main/java/com/bitmovin/player/reactnative/extensions/ReadableMapExtension.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.bitmovin.player.reactnative.extensions - -import com.facebook.react.bridge.* - -fun ReadableMap.getBooleanOrNull(key: String): Boolean? = getValueOrNull(key, ReadableMap::getBoolean) -fun ReadableMap.getIntOrNull(key: String): Int? = getValueOrNull(key, ReadableMap::getInt) -fun ReadableMap.getDoubleOrNull(key: String): Double? = getValueOrNull(key, ReadableMap::getDouble) - -inline fun ReadableMap.getValueOrNull( - key: String, - get: ReadableMap.(String) -> T?, -) = takeIf { hasKey(key) }?.get(key) - -inline fun ReadableMap.withDouble( - key: String, - block: (Double) -> T, -): T? = mapValue(key, ReadableMap::getDouble, block) - -inline fun ReadableMap.withMap( - key: String, - block: (ReadableMap) -> T, -): T? = mapValue(key, ReadableMap::getMap, block) - -inline fun ReadableMap.withInt( - key: String, - block: (Int) -> T, -): T? = mapValue(key, ReadableMap::getInt, block) - -inline fun ReadableMap.withBoolean( - key: String, - block: (Boolean) -> T, -): T? = mapValue(key, ReadableMap::getBoolean, block) - -inline fun ReadableMap.withString( - key: String, - block: (String) -> T, -): T? = mapValue(key, ReadableMap::getString, block) - -inline fun ReadableMap.withArray( - key: String, - block: (ReadableArray) -> T, -): T? = mapValue(key, ReadableMap::getArray, block) - -inline fun ReadableMap.withStringArray( - key: String, - block: (List) -> T, -): T? = mapValue(key, ReadableMap::getStringArray, block) - -fun ReadableMap.getStringArray(it: String): List? = getArray(it)?.toStringList() - -inline fun ReadableMap.mapValue( - key: String, - get: ReadableMap.(String) -> T?, - block: (T) -> R, -) = getValueOrNull(key, get)?.let(block) - -inline fun ReadableMap.toMap(): Map = toHashMap().mapValues { it.value as T } diff --git a/android/src/main/java/com/bitmovin/player/reactnative/extensions/WritableMap.kt b/android/src/main/java/com/bitmovin/player/reactnative/extensions/WritableMap.kt deleted file mode 100644 index 6f17860b..00000000 --- a/android/src/main/java/com/bitmovin/player/reactnative/extensions/WritableMap.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.bitmovin.player.reactnative.extensions - -import com.facebook.react.bridge.WritableMap - -fun WritableMap.putInt(key: String, i: Int?) { - if (i == null) { - putNull(key) - } else { - putInt(key, i) - } -} - -fun WritableMap.putDouble(key: String, d: Double?) { - if (d == null) { - putNull(key) - } else { - putDouble(key, d) - } -} - -fun WritableMap.putBoolean(key: String, b: Boolean?) { - if (b == null) { - putNull(key) - } else { - putBoolean(key, b) - } -} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/offline/OfflineContentManagerBridge.kt b/android/src/main/java/com/bitmovin/player/reactnative/offline/OfflineContentManagerBridge.kt index 97ab272c..3ef494c2 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/offline/OfflineContentManagerBridge.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/offline/OfflineContentManagerBridge.kt @@ -1,5 +1,6 @@ package com.bitmovin.player.reactnative.offline +import android.content.Context import com.bitmovin.player.api.deficiency.ErrorEvent import com.bitmovin.player.api.offline.OfflineContentManager import com.bitmovin.player.api.offline.OfflineContentManagerListener @@ -9,15 +10,13 @@ import com.bitmovin.player.api.offline.options.OfflineOptionEntryAction import com.bitmovin.player.api.offline.options.OfflineOptionEntryState import com.bitmovin.player.api.source.SourceConfig import com.bitmovin.player.reactnative.NativeId +import com.bitmovin.player.reactnative.OfflineModule import com.bitmovin.player.reactnative.converter.toJson -import com.facebook.react.bridge.Arguments -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.WritableMap -import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter class OfflineContentManagerBridge( private val nativeId: NativeId, - private val context: ReactApplicationContext, + context: Context, + private val offlineModule: OfflineModule, private val identifier: String, source: SourceConfig, location: String, @@ -58,8 +57,7 @@ class OfflineContentManagerBridge( */ fun process(request: OfflineDownloadRequest) { if (contentOptions != null) { - val sortedVideoOptions = contentOptions!!.videoOptions - .sortedBy { option -> option.bitrate } + val sortedVideoOptions = contentOptions!!.videoOptions.sortedBy { option -> option.bitrate } if (request.minimumBitrate == null) { sortedVideoOptions.lastOrNull() } else { @@ -135,8 +133,7 @@ class OfflineContentManagerBridge( options?.videoOptions?.let { allOptions.addAll(it) } options?.audioOptions?.let { allOptions.addAll(it) } options?.textOptions?.let { allOptions.addAll(it) } - val trackableStates = - listOf(OfflineOptionEntryState.Suspended, OfflineOptionEntryState.Downloading) + val trackableStates = listOf(OfflineOptionEntryState.Suspended, OfflineOptionEntryState.Downloading) var state = allOptions.firstOrNull { trackableStates.contains(it.state) }?.state @@ -147,86 +144,57 @@ class OfflineContentManagerBridge( return state ?: OfflineOptionEntryState.NotDownloaded } - /** - * Called when a process call has completed. - */ - override fun onCompleted(source: SourceConfig?, options: OfflineContentOptions?) { + override fun onCompleted(source: SourceConfig, options: OfflineContentOptions) { this.contentOptions = options sendEvent( OfflineEventType.ON_COMPLETED, - Arguments.createMap().apply { - putMap("options", options?.toJson()) - }, + mapOf("options" to options.toJson()), ) } - /** - * Called when an error occurs. - */ - override fun onError(source: SourceConfig?, event: ErrorEvent?) { + override fun onError(source: SourceConfig, event: ErrorEvent) { sendEvent( OfflineEventType.ON_ERROR, - Arguments.createMap().apply { - event?.code?.value?.let { putInt("code", it) } - putString("message", event?.message) - }, + mapOf( + "code" to event.code.value, + "message" to event.message, + ), ) } - /** - * Called when the progress for a process call changes. - */ - override fun onProgress(source: SourceConfig?, progress: Float) { + override fun onProgress(source: SourceConfig, progress: Float) { sendEvent( OfflineEventType.ON_PROGRESS, - Arguments.createMap().apply { - putDouble("progress", progress.toDouble()) - }, + mapOf("progress" to progress.toDouble()), ) } - /** - * Called after a getOptions or when am OfflineOptionEntry has been updated during a process call. - */ - override fun onOptionsAvailable(source: SourceConfig?, options: OfflineContentOptions?) { + override fun onOptionsAvailable(source: SourceConfig, options: OfflineContentOptions) { this.contentOptions = options sendEvent( OfflineEventType.ON_OPTIONS_AVAILABLE, - Arguments.createMap().apply { - putMap("options", options?.toJson()) - }, + mapOf("options" to options.toJson()), ) } - /** - * Called when the DRM license was updated. - */ - override fun onDrmLicenseUpdated(source: SourceConfig?) { + override fun onDrmLicenseUpdated(source: SourceConfig) { sendEvent(OfflineEventType.ON_DRM_LICENSE_UPDATED) } - /** - * Called when all actions have been suspended. - */ - override fun onSuspended(source: SourceConfig?) { + override fun onSuspended(source: SourceConfig) { sendEvent(OfflineEventType.ON_SUSPENDED) } - /** - * Called when all actions have been resumed. - */ - override fun onResumed(source: SourceConfig?) { + override fun onResumed(source: SourceConfig) { sendEvent(OfflineEventType.ON_RESUMED) } - private fun sendEvent(eventType: OfflineEventType, event: WritableMap = Arguments.createMap()) { - event.putString("nativeId", nativeId) - event.putString("identifier", identifier) - event.putString("eventType", eventType.eventName) - event.putString("state", aggregateState(contentOptions).name) - context.rtcDeviceEventEmitter.emit("BitmovinOfflineEvent", event) + private fun sendEvent(eventType: OfflineEventType, event: Map = mapOf()) { + val mutableEvent = event.toMutableMap() + mutableEvent["nativeId"] = nativeId + mutableEvent["identifier"] = identifier + mutableEvent["eventType"] = eventType.eventName + mutableEvent["state"] = aggregateState(contentOptions).name + offlineModule.sendEvent("onBitmovinOfflineEvent", mutableEvent) } } - -val ReactApplicationContext.rtcDeviceEventEmitter: RCTDeviceEventEmitter - get() = getJSModule(RCTDeviceEventEmitter::class.java) diff --git a/android/src/main/java/com/bitmovin/player/reactnative/ui/CustomMessageHandlerBridge.kt b/android/src/main/java/com/bitmovin/player/reactnative/ui/CustomMessageHandlerBridge.kt index 2ee86968..130dbb89 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/ui/CustomMessageHandlerBridge.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/ui/CustomMessageHandlerBridge.kt @@ -1,39 +1,29 @@ package com.bitmovin.player.reactnative.ui import android.webkit.JavascriptInterface +import com.bitmovin.player.reactnative.CustomMessageHandlerModule import com.bitmovin.player.reactnative.NativeId -import com.bitmovin.player.reactnative.extensions.getModule import com.bitmovin.player.ui.CustomMessageHandler -import com.facebook.react.bridge.ReactApplicationContext class CustomMessageHandlerBridge( - val context: ReactApplicationContext, private val nativeId: NativeId, + private val module: CustomMessageHandlerModule? = null, ) { val customMessageHandler = CustomMessageHandler( object : Any() { @JavascriptInterface - fun sendSynchronous(name: String, data: String?): String? = context - .getModule() - ?.receivedSynchronousMessage(nativeId, name, data) + fun sendSynchronous( + name: String, + data: String?, + ): String? = module?.receivedSynchronousMessage(nativeId, name, data) @JavascriptInterface - fun sendAsynchronous(name: String, data: String?) = context - .getModule() - ?.receivedAsynchronousMessage(nativeId, name, data) + fun sendAsynchronous( + name: String, + data: String?, + ) = module?.receivedAsynchronousMessage(nativeId, name, data) }, ) - private var currentSynchronousResult: String? = null - fun sendMessage(message: String, data: String?) = customMessageHandler.sendMessage(message, data) - - fun popSynchronousResult(): String? = currentSynchronousResult?.let { - currentSynchronousResult = null - return it - } - - fun pushSynchronousResult(result: String?) { - currentSynchronousResult = result - } } diff --git a/android/src/main/java/com/bitmovin/player/reactnative/ui/CustomMessageHandlerModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/ui/CustomMessageHandlerModule.kt deleted file mode 100644 index 3b709b81..00000000 --- a/android/src/main/java/com/bitmovin/player/reactnative/ui/CustomMessageHandlerModule.kt +++ /dev/null @@ -1,86 +0,0 @@ -package com.bitmovin.player.reactnative.ui - -import com.bitmovin.player.reactnative.NativeId -import com.bitmovin.player.reactnative.Registry -import com.facebook.react.bridge.Arguments -import com.facebook.react.bridge.NativeArray -import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactContextBaseJavaModule -import com.facebook.react.bridge.ReactMethod -import com.facebook.react.module.annotations.ReactModule -import java.util.concurrent.locks.ReentrantLock -import kotlin.concurrent.withLock - -private const val MODULE_NAME = "CustomMessageHandlerModule" - -@ReactModule(name = MODULE_NAME) -class CustomMessageHandlerModule(private val context: ReactApplicationContext) : ReactContextBaseJavaModule(context) { - override fun getName() = MODULE_NAME - - /** - * In-memory mapping from `nativeId`s to `CustomMessageHandler` instances. - */ - private val customMessageHandler: Registry = mutableMapOf() - - /** - * Module's local lock object used to sync calls between Kotlin and JS. - */ - private val lock = ReentrantLock() - - /** - * Lock condition used to sync operations on the fullscreen handler. - */ - private val customMessageHandlerResultChangedCondition = lock.newCondition() - - fun getInstance(nativeId: NativeId?): CustomMessageHandlerBridge? = customMessageHandler[nativeId] - - @ReactMethod(isBlockingSynchronousMethod = true) - fun onReceivedSynchronousMessageResult(nativeId: NativeId, result: String?) { - customMessageHandler[nativeId]?.pushSynchronousResult(result) - lock.withLock { - customMessageHandlerResultChangedCondition.signal() - } - } - - @ReactMethod - fun sendMessage(nativeId: NativeId, message: String, data: String?) { - customMessageHandler[nativeId]?.sendMessage(message, data) - } - - @ReactMethod - fun registerHandler(nativeId: NativeId) { - val customMessageHandler = customMessageHandler[nativeId] ?: CustomMessageHandlerBridge(context, nativeId) - this.customMessageHandler[nativeId] = customMessageHandler - } - - @ReactMethod - fun destroy(nativeId: NativeId) { - customMessageHandler.remove(nativeId) - } - - fun receivedSynchronousMessage(nativeId: NativeId, message: String, data: String?): String? { - val args = Arguments.createArray() - args.pushString(message) - args.pushString(data) - lock.withLock { - context.catalystInstance.callFunction( - "CustomMessageBridge-$nativeId", - "receivedSynchronousMessage", - args as NativeArray, - ) - customMessageHandlerResultChangedCondition.await() - } - return customMessageHandler[nativeId]?.popSynchronousResult() - } - - fun receivedAsynchronousMessage(nativeId: NativeId, message: String, data: String?) { - val args = Arguments.createArray() - args.pushString(message) - args.pushString(data) - context.catalystInstance.callFunction( - "CustomMessageBridge-$nativeId", - "receivedAsynchronousMessage", - args as NativeArray, - ) - } -} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerBridge.kt b/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerBridge.kt index a16bd078..c19b204c 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerBridge.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerBridge.kt @@ -1,13 +1,12 @@ package com.bitmovin.player.reactnative.ui import com.bitmovin.player.api.ui.FullscreenHandler +import com.bitmovin.player.reactnative.FullscreenHandlerModule import com.bitmovin.player.reactnative.NativeId -import com.bitmovin.player.reactnative.extensions.getModule -import com.facebook.react.bridge.ReactApplicationContext class FullscreenHandlerBridge( - val context: ReactApplicationContext, private val nativeId: NativeId, + private val module: FullscreenHandlerModule? = null, ) : FullscreenHandler { override var isFullscreen = false @@ -16,15 +15,11 @@ class FullscreenHandlerBridge( } override fun onFullscreenExitRequested() { - context - .getModule() - ?.requestExitFullscreen(nativeId) + module?.requestExitFullscreen(nativeId) } override fun onFullscreenRequested() { - context - .getModule() - ?.requestEnterFullscreen(nativeId) + module?.requestEnterFullscreen(nativeId) } override fun onPause() { diff --git a/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerModule.kt b/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerModule.kt deleted file mode 100644 index 022bd5f3..00000000 --- a/android/src/main/java/com/bitmovin/player/reactnative/ui/FullscreenHandlerModule.kt +++ /dev/null @@ -1,78 +0,0 @@ -package com.bitmovin.player.reactnative.ui - -import com.bitmovin.player.reactnative.NativeId -import com.bitmovin.player.reactnative.Registry -import com.facebook.react.bridge.* -import com.facebook.react.module.annotations.ReactModule -import java.util.concurrent.locks.ReentrantLock -import kotlin.concurrent.withLock - -private const val MODULE_NAME = "FullscreenHandlerModule" - -@ReactModule(name = MODULE_NAME) -class FullscreenHandlerModule(private val context: ReactApplicationContext) : ReactContextBaseJavaModule(context) { - override fun getName() = MODULE_NAME - - /** - * In-memory mapping from `nativeId`s to `FullscreenHandler` instances. - */ - private val fullscreenHandler: Registry = mutableMapOf() - - /** - * Module's local lock object used to sync calls between Kotlin and JS. - */ - private val lock = ReentrantLock() - - /** - * Lock condition used to sync operations on the fullscreen handler. - */ - private val fullscreenChangedCondition = lock.newCondition() - - fun getInstance(nativeId: NativeId?): FullscreenHandlerBridge? = fullscreenHandler[nativeId] - - fun requestEnterFullscreen(nativeId: NativeId) { - context.catalystInstance.callFunction( - "FullscreenBridge-$nativeId", - "enterFullscreen", - Arguments.createArray() as NativeArray, - ) - lock.withLock { - fullscreenChangedCondition.await() - } - } - - fun requestExitFullscreen(nativeId: NativeId) { - context.catalystInstance.callFunction( - "FullscreenBridge-$nativeId", - "exitFullscreen", - Arguments.createArray() as NativeArray, - ) - lock.withLock { - fullscreenChangedCondition.await() - } - } - - @ReactMethod(isBlockingSynchronousMethod = true) - fun onFullscreenChanged(nativeId: NativeId, isFullscreenEnabled: Boolean) { - fullscreenHandler[nativeId]?.isFullscreen = isFullscreenEnabled - lock.withLock { - fullscreenChangedCondition.signal() - } - } - - @ReactMethod - fun registerHandler(nativeId: NativeId) { - val fullscreenHandler = fullscreenHandler[nativeId] ?: FullscreenHandlerBridge(context, nativeId) - this.fullscreenHandler[nativeId] = fullscreenHandler - } - - @ReactMethod - fun setIsFullscreenActive(nativeId: NativeId, isFullscreenActive: Boolean) { - fullscreenHandler[nativeId]?.isFullscreen = isFullscreenActive - } - - @ReactMethod - fun destroy(nativeId: NativeId) { - fullscreenHandler.remove(nativeId) - } -} diff --git a/android/src/main/java/com/bitmovin/player/reactnative/ui/RNPictureInPictureHandler.kt b/android/src/main/java/com/bitmovin/player/reactnative/ui/RNPictureInPictureHandler.kt index f48342e8..fcbac147 100644 --- a/android/src/main/java/com/bitmovin/player/reactnative/ui/RNPictureInPictureHandler.kt +++ b/android/src/main/java/com/bitmovin/player/reactnative/ui/RNPictureInPictureHandler.kt @@ -10,8 +10,8 @@ import com.bitmovin.player.ui.DefaultPictureInPictureHandler private const val TAG = "RNPiPHandler" class RNPictureInPictureHandler( - activity: Activity, - player: Player, + private val activity: Activity, + private val player: Player, ) : DefaultPictureInPictureHandler(activity, player) { // Current PiP implementation on the native side requires playerView.exitPictureInPicture() to be called // for `PictureInPictureExit` event to be emitted. diff --git a/android/src/main/java/com/bitmovin/player/reactnative/util/NonFiniteSanitizer.kt b/android/src/main/java/com/bitmovin/player/reactnative/util/NonFiniteSanitizer.kt new file mode 100644 index 00000000..8b705d0d --- /dev/null +++ b/android/src/main/java/com/bitmovin/player/reactnative/util/NonFiniteSanitizer.kt @@ -0,0 +1,36 @@ +package com.bitmovin.player.reactnative.util + +object NonFiniteSanitizer { + /** + * Type-safe method specifically for event data maps. + * Sanitizes event data while preserving type safety and handling null filtering. + */ + fun sanitizeEventData( + eventData: Map, + ): Map = eventData.mapValues { sanitize(it.value) ?: it.value } + + private fun sanitize(value: Any?): Any? = when (value) { + null -> null + is Double -> if (value.isFinite()) value else value.toSentinel() + is Float -> if (value.isFinite()) value else value.toSentinel() + is Map<*, *> -> value.mapValues { sanitize(it.value) } + is List<*> -> value.mapNotNull { sanitize(it) } + is Array<*> -> value.mapNotNull { sanitize(it) } + is Number -> value // Int/Long/Short/Byte + else -> value + } +} + +private const val SENTINEL_PREFIX = "BMP_" + +private fun Double.toSentinel(): String = when (this) { + Double.POSITIVE_INFINITY -> "${SENTINEL_PREFIX}Infinity" + Double.NEGATIVE_INFINITY -> "${SENTINEL_PREFIX}-Infinity" + else -> "${SENTINEL_PREFIX}NaN" +} + +private fun Float.toSentinel(): String = when (this) { + Float.POSITIVE_INFINITY -> "${SENTINEL_PREFIX}Infinity" + Float.NEGATIVE_INFINITY -> "${SENTINEL_PREFIX}-Infinity" + else -> "${SENTINEL_PREFIX}NaN" +} \ No newline at end of file diff --git a/app.plugin.js b/app.plugin.js new file mode 100644 index 00000000..4d65d5d1 --- /dev/null +++ b/app.plugin.js @@ -0,0 +1,21 @@ +/** + * Expo plugin for bitmovin-player-react-native + * + * Example: + * "plugins": [ + * [ + * "bitmovin-player-react-native", + * { + * "licenseKey": "ENTER_LICENSE_KEY", + * "featureFlags": { + * "airPlay": true, + * "backgroundPlayback": true, + * "googleCastSDK": { "android": "21.3.0", "ios": "4.8.1.2" }, + * "offline": true, + * "pictureInPicture": true + * } + * } + * ] + * ] + */ +module.exports = require('./plugin/build'); diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index 98655448..00000000 --- a/babel.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - presets: ['@react-native/babel-preset'], -}; diff --git a/build/BitmovinPlayerReactNative.types.d.ts b/build/BitmovinPlayerReactNative.types.d.ts new file mode 100644 index 00000000..4f4d712b --- /dev/null +++ b/build/BitmovinPlayerReactNative.types.d.ts @@ -0,0 +1,18 @@ +import type { StyleProp, ViewStyle } from 'react-native'; +export type OnLoadEventPayload = { + url: string; +}; +export type BitmovinPlayerReactNativeModuleEvents = { + onChange: (params: ChangeEventPayload) => void; +}; +export type ChangeEventPayload = { + value: string; +}; +export type BitmovinPlayerReactNativeViewProps = { + url: string; + onLoad: (event: { + nativeEvent: OnLoadEventPayload; + }) => void; + style?: StyleProp; +}; +//# sourceMappingURL=BitmovinPlayerReactNative.types.d.ts.map \ No newline at end of file diff --git a/build/BitmovinPlayerReactNative.types.d.ts.map b/build/BitmovinPlayerReactNative.types.d.ts.map new file mode 100644 index 00000000..5bf5a332 --- /dev/null +++ b/build/BitmovinPlayerReactNative.types.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"BitmovinPlayerReactNative.types.d.ts","sourceRoot":"","sources":["../src/BitmovinPlayerReactNative.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,qCAAqC,GAAG;IAClD,QAAQ,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAAG;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE,kBAAkB,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7D,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B,CAAC"} \ No newline at end of file diff --git a/build/BitmovinPlayerReactNative.types.js b/build/BitmovinPlayerReactNative.types.js new file mode 100644 index 00000000..753b6ac0 --- /dev/null +++ b/build/BitmovinPlayerReactNative.types.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=BitmovinPlayerReactNative.types.js.map \ No newline at end of file diff --git a/build/BitmovinPlayerReactNative.types.js.map b/build/BitmovinPlayerReactNative.types.js.map new file mode 100644 index 00000000..ea1d8ec9 --- /dev/null +++ b/build/BitmovinPlayerReactNative.types.js.map @@ -0,0 +1 @@ +{"version":3,"file":"BitmovinPlayerReactNative.types.js","sourceRoot":"","sources":["../src/BitmovinPlayerReactNative.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { StyleProp, ViewStyle } from 'react-native';\n\nexport type OnLoadEventPayload = {\n url: string;\n};\n\nexport type BitmovinPlayerReactNativeModuleEvents = {\n onChange: (params: ChangeEventPayload) => void;\n};\n\nexport type ChangeEventPayload = {\n value: string;\n};\n\nexport type BitmovinPlayerReactNativeViewProps = {\n url: string;\n onLoad: (event: { nativeEvent: OnLoadEventPayload }) => void;\n style?: StyleProp;\n};\n"]} \ No newline at end of file diff --git a/build/adaptationConfig.d.ts b/build/adaptationConfig.d.ts new file mode 100644 index 00000000..f2ead179 --- /dev/null +++ b/build/adaptationConfig.d.ts @@ -0,0 +1,18 @@ +/** + * Configures the adaptation logic. + */ +export interface AdaptationConfig { + /** + * The upper bitrate boundary in bits per second for approximate network bandwidth consumption of the played source. + * Can be set to `undefined` for no limitation. + */ + maxSelectableBitrate?: number; + /** + * The initial bandwidth estimate in bits per second the player uses to select the optimal media tracks before actual bandwidth data is available. Overriding this value should only be done in specific cases and will most of the time not result in better selection logic. + * + * @remarks Platform: Android + * @see https://cdn.bitmovin.com/player/android/3/docs/player-core/com.bitmovin.player.api.media/-adaptation-config/initial-bandwidth-estimate-override.html + */ + initialBandwidthEstimateOverride?: number; +} +//# sourceMappingURL=adaptationConfig.d.ts.map \ No newline at end of file diff --git a/build/adaptationConfig.d.ts.map b/build/adaptationConfig.d.ts.map new file mode 100644 index 00000000..18a79a7e --- /dev/null +++ b/build/adaptationConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"adaptationConfig.d.ts","sourceRoot":"","sources":["../src/adaptationConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;;;OAKG;IACH,gCAAgC,CAAC,EAAE,MAAM,CAAC;CAC3C"} \ No newline at end of file diff --git a/build/adaptationConfig.js b/build/adaptationConfig.js new file mode 100644 index 00000000..5aafc49b --- /dev/null +++ b/build/adaptationConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=adaptationConfig.js.map \ No newline at end of file diff --git a/build/adaptationConfig.js.map b/build/adaptationConfig.js.map new file mode 100644 index 00000000..ba24edb3 --- /dev/null +++ b/build/adaptationConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"adaptationConfig.js","sourceRoot":"","sources":["../src/adaptationConfig.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Configures the adaptation logic.\n */\nexport interface AdaptationConfig {\n /**\n * The upper bitrate boundary in bits per second for approximate network bandwidth consumption of the played source.\n * Can be set to `undefined` for no limitation.\n */\n maxSelectableBitrate?: number;\n\n /**\n * The initial bandwidth estimate in bits per second the player uses to select the optimal media tracks before actual bandwidth data is available. Overriding this value should only be done in specific cases and will most of the time not result in better selection logic.\n *\n * @remarks Platform: Android\n * @see https://cdn.bitmovin.com/player/android/3/docs/player-core/com.bitmovin.player.api.media/-adaptation-config/initial-bandwidth-estimate-override.html\n */\n initialBandwidthEstimateOverride?: number;\n}\n"]} \ No newline at end of file diff --git a/build/advertising.d.ts b/build/advertising.d.ts new file mode 100644 index 00000000..e01d88f1 --- /dev/null +++ b/build/advertising.d.ts @@ -0,0 +1,175 @@ +/** + * Quartiles that can be reached during an ad playback. + */ +export declare enum AdQuartile { + /** + * Fist ad quartile. + */ + FIRST = "first", + /** + * Mid ad quartile. + */ + MID_POINT = "mid_point", + /** + * Third ad quartile. + */ + THIRD = "third" +} +/** + * The possible types an `AdSource` can be. + */ +export declare enum AdSourceType { + /** + * Google Interactive Media Ads. + */ + IMA = "ima", + /** + * Unknown ad source type. + */ + UNKNOWN = "unknown", + /** + * Progressive ad type. + */ + PROGRESSIVE = "progressive" +} +/** + * Represents an ad source which can be assigned to an `AdItem`. An `AdItem` can have multiple `AdSource`s + * as waterfalling option. + */ +export interface AdSource { + /** + * The ad tag / url to the ad manifest. + */ + tag: string; + /** + * The `AdSourceType` of this `AdSource`. + */ + type: AdSourceType; +} +/** + * Represents an ad break which can be scheduled for playback. + * + * One single `AdItem` can have multiple `AdSource`s where all but the first act as fallback ad sources + * if the first one fails to load. The start and end of an ad break are signaled via `AdBreakStartedEvent` + * and `AdBreakFinishedEvent`. + */ +export interface AdItem { + /** + * The playback position at which the ad break is scheduled to start. Default value is "pre". + * + * Possible values are: + * • "pre": pre-roll ad (for VoD and Live streaming) + * • "post": post-roll ad (for VoD streaming only) + * • fractional seconds: "10", "12.5" (mid-roll ad, for VoD and Live streaming) + * • percentage of the entire video duration: "25%", "50%" (mid-roll ad, for VoD streaming only) + * • timecode hh:mm:ss.mmm: "00:10:30.000", "01:00:00.000" (mid-roll ad, for VoD streaming only) + */ + position?: string; + /** + * The `AdSource`s that make up this `AdItem`. The first ad source in this array is used as the main ad. + * Subsequent ad sources act as a fallback, meaning that if the main ad source does not provide a + * valid response, the subsequent ad sources will be utilized one after another. + * + * The fallback ad sources need to have the same `AdSourceType` as the main ad source. + */ + sources: AdSource[]; + /** + * The amount of seconds the ad manifest is loaded in advance + * compared to when the ad break is scheduled for playback. + * + * Default value is 0.0 + * + * @remarks Platform: Android + */ + preloadOffset?: number; +} +/** + * Contains configuration values regarding the ads which should be played back by the player. + */ +export interface AdvertisingConfig { + /** + * The ad items that are scheduled when a new playback session is started via `Player.load()`. + */ + schedule: AdItem[]; +} +/** + * Contains the base configuration options for an ad. + */ +export interface AdConfig { + /** + * Specifies how many seconds of the main video content should be replaced by ad break(s). + */ + replaceContentDuration: number; +} +/** + * Holds various additional ad data. + */ +export interface AdData { + /** + * The average bitrate of the progressive media file as defined in the VAST response. + */ + bitrate?: number; + /** + * The maximum bitrate of the streaming media file as defined in the VAST response. + */ + maxBitrate?: number; + /** + * The MIME type of the media file or creative as defined in the VAST response. + */ + mimeType?: string; + /** + * The minimum bitrate of the streaming media file as defined in the VAST response. + */ + minBitrate?: number; +} +/** + * Defines basic properties available for every ad type. + */ +export interface Ad { + /** + * The url the user should be redirected to when clicking the ad. + */ + clickThroughUrl?: string; + /** + * Holds various additional `AdData`. + */ + data?: AdData; + /** + * The height of the ad. + */ + height: number; + /** + * Identifier for the ad. This might be autogenerated. + */ + id?: string; + /** + * Determines whether an ad is linear, i.e. playback of main content needs to be paused for the ad. + */ + isLinear: boolean; + /** + * The corresponding media file url for the ad. + */ + mediaFileUrl?: string; + /** + * The width of the ad. + */ + width: number; +} +/** + * Contains information about an ad break. + */ +export interface AdBreak { + /** + * The ads scheduled for this `AdBreak`. + */ + ads: Ad[]; + /** + * The id of the corresponding `AdBreakConfig`. This will be auto-generated. + */ + id: string; + /** + * The time in seconds in the media timeline the `AdBreak` is scheduled for. + */ + scheduleTime: number; +} +//# sourceMappingURL=advertising.d.ts.map \ No newline at end of file diff --git a/build/advertising.d.ts.map b/build/advertising.d.ts.map new file mode 100644 index 00000000..4a782ef4 --- /dev/null +++ b/build/advertising.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"advertising.d.ts","sourceRoot":"","sources":["../src/advertising.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,oBAAY,UAAU;IACpB;;OAEG;IACH,KAAK,UAAU;IACf;;OAEG;IACH,SAAS,cAAc;IACvB;;OAEG;IACH,KAAK,UAAU;CAChB;AAED;;GAEG;AACH,oBAAY,YAAY;IACtB;;OAEG;IACH,GAAG,QAAQ;IACX;;OAEG;IACH,OAAO,YAAY;IACnB;;OAEG;IACH,WAAW,gBAAgB;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,IAAI,EAAE,YAAY,CAAC;CACpB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,MAAM;IACrB;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;OAMG;IACH,OAAO,EAAE,QAAQ,EAAE,CAAC;IAEpB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,EAAE;IACjB;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB;;OAEG;IACH,GAAG,EAAE,EAAE,EAAE,CAAC;IACV;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IACX;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;CACtB"} \ No newline at end of file diff --git a/build/advertising.js b/build/advertising.js new file mode 100644 index 00000000..30dbba96 --- /dev/null +++ b/build/advertising.js @@ -0,0 +1,37 @@ +/** + * Quartiles that can be reached during an ad playback. + */ +export var AdQuartile; +(function (AdQuartile) { + /** + * Fist ad quartile. + */ + AdQuartile["FIRST"] = "first"; + /** + * Mid ad quartile. + */ + AdQuartile["MID_POINT"] = "mid_point"; + /** + * Third ad quartile. + */ + AdQuartile["THIRD"] = "third"; +})(AdQuartile || (AdQuartile = {})); +/** + * The possible types an `AdSource` can be. + */ +export var AdSourceType; +(function (AdSourceType) { + /** + * Google Interactive Media Ads. + */ + AdSourceType["IMA"] = "ima"; + /** + * Unknown ad source type. + */ + AdSourceType["UNKNOWN"] = "unknown"; + /** + * Progressive ad type. + */ + AdSourceType["PROGRESSIVE"] = "progressive"; +})(AdSourceType || (AdSourceType = {})); +//# sourceMappingURL=advertising.js.map \ No newline at end of file diff --git a/build/advertising.js.map b/build/advertising.js.map new file mode 100644 index 00000000..dd55cafa --- /dev/null +++ b/build/advertising.js.map @@ -0,0 +1 @@ +{"version":3,"file":"advertising.js","sourceRoot":"","sources":["../src/advertising.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAN,IAAY,UAaX;AAbD,WAAY,UAAU;IACpB;;OAEG;IACH,6BAAe,CAAA;IACf;;OAEG;IACH,qCAAuB,CAAA;IACvB;;OAEG;IACH,6BAAe,CAAA;AACjB,CAAC,EAbW,UAAU,KAAV,UAAU,QAarB;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,YAaX;AAbD,WAAY,YAAY;IACtB;;OAEG;IACH,2BAAW,CAAA;IACX;;OAEG;IACH,mCAAmB,CAAA;IACnB;;OAEG;IACH,2CAA2B,CAAA;AAC7B,CAAC,EAbW,YAAY,KAAZ,YAAY,QAavB","sourcesContent":["/**\n * Quartiles that can be reached during an ad playback.\n */\nexport enum AdQuartile {\n /**\n * Fist ad quartile.\n */\n FIRST = 'first',\n /**\n * Mid ad quartile.\n */\n MID_POINT = 'mid_point',\n /**\n * Third ad quartile.\n */\n THIRD = 'third',\n}\n\n/**\n * The possible types an `AdSource` can be.\n */\nexport enum AdSourceType {\n /**\n * Google Interactive Media Ads.\n */\n IMA = 'ima',\n /**\n * Unknown ad source type.\n */\n UNKNOWN = 'unknown',\n /**\n * Progressive ad type.\n */\n PROGRESSIVE = 'progressive',\n}\n\n/**\n * Represents an ad source which can be assigned to an `AdItem`. An `AdItem` can have multiple `AdSource`s\n * as waterfalling option.\n */\nexport interface AdSource {\n /**\n * The ad tag / url to the ad manifest.\n */\n tag: string;\n /**\n * The `AdSourceType` of this `AdSource`.\n */\n type: AdSourceType;\n}\n\n/**\n * Represents an ad break which can be scheduled for playback.\n *\n * One single `AdItem` can have multiple `AdSource`s where all but the first act as fallback ad sources\n * if the first one fails to load. The start and end of an ad break are signaled via `AdBreakStartedEvent`\n * and `AdBreakFinishedEvent`.\n */\nexport interface AdItem {\n /**\n * The playback position at which the ad break is scheduled to start. Default value is \"pre\".\n *\n * Possible values are:\n * • \"pre\": pre-roll ad (for VoD and Live streaming)\n * • \"post\": post-roll ad (for VoD streaming only)\n * • fractional seconds: \"10\", \"12.5\" (mid-roll ad, for VoD and Live streaming)\n * • percentage of the entire video duration: \"25%\", \"50%\" (mid-roll ad, for VoD streaming only)\n * • timecode hh:mm:ss.mmm: \"00:10:30.000\", \"01:00:00.000\" (mid-roll ad, for VoD streaming only)\n */\n position?: string;\n /**\n * The `AdSource`s that make up this `AdItem`. The first ad source in this array is used as the main ad.\n * Subsequent ad sources act as a fallback, meaning that if the main ad source does not provide a\n * valid response, the subsequent ad sources will be utilized one after another.\n *\n * The fallback ad sources need to have the same `AdSourceType` as the main ad source.\n */\n sources: AdSource[];\n\n /**\n * The amount of seconds the ad manifest is loaded in advance\n * compared to when the ad break is scheduled for playback.\n *\n * Default value is 0.0\n *\n * @remarks Platform: Android\n */\n preloadOffset?: number;\n}\n\n/**\n * Contains configuration values regarding the ads which should be played back by the player.\n */\nexport interface AdvertisingConfig {\n /**\n * The ad items that are scheduled when a new playback session is started via `Player.load()`.\n */\n schedule: AdItem[];\n}\n\n/**\n * Contains the base configuration options for an ad.\n */\nexport interface AdConfig {\n /**\n * Specifies how many seconds of the main video content should be replaced by ad break(s).\n */\n replaceContentDuration: number;\n}\n\n/**\n * Holds various additional ad data.\n */\nexport interface AdData {\n /**\n * The average bitrate of the progressive media file as defined in the VAST response.\n */\n bitrate?: number;\n /**\n * The maximum bitrate of the streaming media file as defined in the VAST response.\n */\n maxBitrate?: number;\n /**\n * The MIME type of the media file or creative as defined in the VAST response.\n */\n mimeType?: string;\n /**\n * The minimum bitrate of the streaming media file as defined in the VAST response.\n */\n minBitrate?: number;\n}\n\n/**\n * Defines basic properties available for every ad type.\n */\nexport interface Ad {\n /**\n * The url the user should be redirected to when clicking the ad.\n */\n clickThroughUrl?: string;\n /**\n * Holds various additional `AdData`.\n */\n data?: AdData;\n /**\n * The height of the ad.\n */\n height: number;\n /**\n * Identifier for the ad. This might be autogenerated.\n */\n id?: string;\n /**\n * Determines whether an ad is linear, i.e. playback of main content needs to be paused for the ad.\n */\n isLinear: boolean;\n /**\n * The corresponding media file url for the ad.\n */\n mediaFileUrl?: string;\n /**\n * The width of the ad.\n */\n width: number;\n}\n\n/**\n * Contains information about an ad break.\n */\nexport interface AdBreak {\n /**\n * The ads scheduled for this `AdBreak`.\n */\n ads: Ad[];\n /**\n * The id of the corresponding `AdBreakConfig`. This will be auto-generated.\n */\n id: string;\n /**\n * The time in seconds in the media timeline the `AdBreak` is scheduled for.\n */\n scheduleTime: number;\n}\n"]} \ No newline at end of file diff --git a/build/analytics/config.d.ts b/build/analytics/config.d.ts new file mode 100644 index 00000000..5cd7b2ba --- /dev/null +++ b/build/analytics/config.d.ts @@ -0,0 +1,194 @@ +/** + * Object used to configure the build-in analytics collector. + */ +export interface AnalyticsConfig { + /** + * The analytics license key + */ + licenseKey: string; + /** + * Flag to enable Ad tracking (default: false). + */ + adTrackingDisabled?: boolean; + /** + * Flag to use randomised userId not depending on device specific values (default: false). + */ + randomizeUserId?: boolean; + /** + * Default metadata to be sent with events. + * Fields of the `SourceMetadata` are prioritized over the default metadata. + */ + defaultMetadata?: DefaultMetadata; +} +/** + * DefaultMetadata that can be used to enrich the analytics data. + * DefaultMetadata is not bound to a specific source and can be used to set fields for the lifecycle of the collector. + * If fields are specified in `SourceMetadata` and `DefaultMetadata`, `SourceMetadata` takes precedence. + */ +export interface DefaultMetadata extends CustomDataConfig { + /** + * CDN Provide that the video playback session is using. + */ + cdnProvider?: string; + /** + * User ID of the customer. + */ + customUserId?: string; +} +/** + * `SourceMetadata` that can be used to enrich the analytics data. + */ +export interface SourceMetadata extends CustomDataConfig { + /** + * ID of the video in the CMS system + */ + videoId?: string; + /** + * Human readable title of the video asset currently playing + */ + title?: string; + /** + * Breadcrumb path to show where in the app the user is + */ + path?: string; + /** + * Flag to see if stream is live before stream metadata is available + */ + isLive?: boolean; + /** + * CDN Provider that the video playback session is using + */ + cdnProvider?: string; +} +/** + * Free-form data that can be used to enrich the analytics data + * If customData is specified in `SourceMetadata` and `DefaultMetadata` + * data is merged on a field basis with `SourceMetadata` taking precedence. + */ +export interface CustomDataConfig { + /** + * Optional free-form custom data + */ + customData1?: string; + /** + * Optional free-form custom data + */ + customData2?: string; + /** + * Optional free-form custom data + */ + customData3?: string; + /** + * Optional free-form custom data + */ + customData4?: string; + /** + * Optional free-form custom data + */ + customData5?: string; + /** + * Optional free-form custom data + */ + customData6?: string; + /** + * Optional free-form custom data + */ + customData7?: string; + /** + * Optional free-form custom data + */ + customData8?: string; + /** + * Optional free-form custom data + */ + customData9?: string; + /** + * Optional free-form custom data + */ + customData10?: string; + /** + * Optional free-form custom data + */ + customData11?: string; + /** + * Optional free-form custom data + */ + customData12?: string; + /** + * Optional free-form custom data + */ + customData13?: string; + /** + * Optional free-form custom data + */ + customData14?: string; + /** + * Optional free-form custom data + */ + customData15?: string; + /** + * Optional free-form custom data + */ + customData16?: string; + /** + * Optional free-form custom data + */ + customData17?: string; + /** + * Optional free-form custom data + */ + customData18?: string; + /** + * Optional free-form custom data + */ + customData19?: string; + /** + * Optional free-form custom data + */ + customData20?: string; + /** + * Optional free-form custom data + */ + customData21?: string; + /** + * Optional free-form custom data + */ + customData22?: string; + /** + * Optional free-form custom data + */ + customData23?: string; + /** + * Optional free-form custom data + */ + customData24?: string; + /** + * Optional free-form custom data + */ + customData25?: string; + /** + * Optional free-form custom data + */ + customData26?: string; + /** + * Optional free-form custom data + */ + customData27?: string; + /** + * Optional free-form custom data + */ + customData28?: string; + /** + * Optional free-form custom data + */ + customData29?: string; + /** + * Optional free-form custom data + */ + customData30?: string; + /** + * Experiment name needed for A/B testing. + */ + experimentName?: string; +} +//# sourceMappingURL=config.d.ts.map \ No newline at end of file diff --git a/build/analytics/config.d.ts.map b/build/analytics/config.d.ts.map new file mode 100644 index 00000000..b548fba7 --- /dev/null +++ b/build/analytics/config.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/analytics/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACvD;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,cAAe,SAAQ,gBAAgB;IACtD;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"} \ No newline at end of file diff --git a/build/analytics/config.js b/build/analytics/config.js new file mode 100644 index 00000000..79bd47be --- /dev/null +++ b/build/analytics/config.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=config.js.map \ No newline at end of file diff --git a/build/analytics/config.js.map b/build/analytics/config.js.map new file mode 100644 index 00000000..510cda32 --- /dev/null +++ b/build/analytics/config.js.map @@ -0,0 +1 @@ +{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/analytics/config.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Object used to configure the build-in analytics collector.\n */\nexport interface AnalyticsConfig {\n /**\n * The analytics license key\n */\n licenseKey: string;\n /**\n * Flag to enable Ad tracking (default: false).\n */\n adTrackingDisabled?: boolean;\n /**\n * Flag to use randomised userId not depending on device specific values (default: false).\n */\n randomizeUserId?: boolean;\n /**\n * Default metadata to be sent with events.\n * Fields of the `SourceMetadata` are prioritized over the default metadata.\n */\n defaultMetadata?: DefaultMetadata;\n}\n\n/**\n * DefaultMetadata that can be used to enrich the analytics data.\n * DefaultMetadata is not bound to a specific source and can be used to set fields for the lifecycle of the collector.\n * If fields are specified in `SourceMetadata` and `DefaultMetadata`, `SourceMetadata` takes precedence.\n */\nexport interface DefaultMetadata extends CustomDataConfig {\n /**\n * CDN Provide that the video playback session is using.\n */\n cdnProvider?: string;\n /**\n * User ID of the customer.\n */\n customUserId?: string;\n}\n\n/**\n * `SourceMetadata` that can be used to enrich the analytics data.\n */\nexport interface SourceMetadata extends CustomDataConfig {\n /**\n * ID of the video in the CMS system\n */\n videoId?: string;\n\n /**\n * Human readable title of the video asset currently playing\n */\n title?: string;\n\n /**\n * Breadcrumb path to show where in the app the user is\n */\n path?: string;\n\n /**\n * Flag to see if stream is live before stream metadata is available\n */\n isLive?: boolean;\n\n /**\n * CDN Provider that the video playback session is using\n */\n cdnProvider?: string;\n}\n\n/**\n * Free-form data that can be used to enrich the analytics data\n * If customData is specified in `SourceMetadata` and `DefaultMetadata`\n * data is merged on a field basis with `SourceMetadata` taking precedence.\n */\nexport interface CustomDataConfig {\n /**\n * Optional free-form custom data\n */\n customData1?: string;\n\n /**\n * Optional free-form custom data\n */\n customData2?: string;\n\n /**\n * Optional free-form custom data\n */\n customData3?: string;\n\n /**\n * Optional free-form custom data\n */\n customData4?: string;\n\n /**\n * Optional free-form custom data\n */\n customData5?: string;\n\n /**\n * Optional free-form custom data\n */\n customData6?: string;\n\n /**\n * Optional free-form custom data\n */\n customData7?: string;\n\n /**\n * Optional free-form custom data\n */\n customData8?: string;\n\n /**\n * Optional free-form custom data\n */\n customData9?: string;\n\n /**\n * Optional free-form custom data\n */\n customData10?: string;\n\n /**\n * Optional free-form custom data\n */\n customData11?: string;\n\n /**\n * Optional free-form custom data\n */\n customData12?: string;\n\n /**\n * Optional free-form custom data\n */\n customData13?: string;\n\n /**\n * Optional free-form custom data\n */\n customData14?: string;\n\n /**\n * Optional free-form custom data\n */\n customData15?: string;\n\n /**\n * Optional free-form custom data\n */\n customData16?: string;\n\n /**\n * Optional free-form custom data\n */\n customData17?: string;\n\n /**\n * Optional free-form custom data\n */\n customData18?: string;\n\n /**\n * Optional free-form custom data\n */\n customData19?: string;\n\n /**\n * Optional free-form custom data\n */\n customData20?: string;\n\n /**\n * Optional free-form custom data\n */\n customData21?: string;\n\n /**\n * Optional free-form custom data\n */\n customData22?: string;\n\n /**\n * Optional free-form custom data\n */\n customData23?: string;\n\n /**\n * Optional free-form custom data\n */\n customData24?: string;\n\n /**\n * Optional free-form custom data\n */\n customData25?: string;\n\n /**\n * Optional free-form custom data\n */\n customData26?: string;\n\n /**\n * Optional free-form custom data\n */\n customData27?: string;\n\n /**\n * Optional free-form custom data\n */\n customData28?: string;\n\n /**\n * Optional free-form custom data\n */\n customData29?: string;\n\n /**\n * Optional free-form custom data\n */\n customData30?: string;\n\n /**\n * Experiment name needed for A/B testing.\n */\n experimentName?: string;\n}\n"]} \ No newline at end of file diff --git a/build/analytics/index.d.ts b/build/analytics/index.d.ts new file mode 100644 index 00000000..f2290d14 --- /dev/null +++ b/build/analytics/index.d.ts @@ -0,0 +1,3 @@ +export * from './config'; +export * from './player'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/build/analytics/index.d.ts.map b/build/analytics/index.d.ts.map new file mode 100644 index 00000000..bd66c8d2 --- /dev/null +++ b/build/analytics/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC"} \ No newline at end of file diff --git a/build/analytics/index.js b/build/analytics/index.js new file mode 100644 index 00000000..10a1efa7 --- /dev/null +++ b/build/analytics/index.js @@ -0,0 +1,3 @@ +export * from './config'; +export * from './player'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/build/analytics/index.js.map b/build/analytics/index.js.map new file mode 100644 index 00000000..b93e1161 --- /dev/null +++ b/build/analytics/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/analytics/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC","sourcesContent":["export * from './config';\nexport * from './player';\n"]} \ No newline at end of file diff --git a/build/analytics/player.d.ts b/build/analytics/player.d.ts new file mode 100644 index 00000000..a0bbf8fe --- /dev/null +++ b/build/analytics/player.d.ts @@ -0,0 +1,24 @@ +import { CustomDataConfig } from './config'; +/** + * Provides the means to control the analytics collected by a `Player`. + * Use the `Player.analytics` property to access a `Player`'s `AnalyticsApi`. + */ +export declare class AnalyticsApi { + /** + * The native player id that this analytics api is attached to. + */ + playerId: string; + constructor(playerId: string); + /** + * Sends a sample with the provided custom data. + * Does not change the configured custom data of the collector or source. + */ + sendCustomDataEvent: (customData: CustomDataConfig) => Promise; + /** + * Gets the current user id used by the bundled analytics instance. + * + * @returns The current user id. + */ + getUserId: () => Promise; +} +//# sourceMappingURL=player.d.ts.map \ No newline at end of file diff --git a/build/analytics/player.d.ts.map b/build/analytics/player.d.ts.map new file mode 100644 index 00000000..7973525f --- /dev/null +++ b/build/analytics/player.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"player.d.ts","sourceRoot":"","sources":["../../src/analytics/player.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAG5C;;;GAGG;AACH,qBAAa,YAAY;IACvB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;gBAEL,QAAQ,EAAE,MAAM;IAI5B;;;OAGG;IACH,mBAAmB,GAAU,YAAY,gBAAgB,mBAEvD;IAEF;;;;OAIG;IACH,SAAS,QAAa,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAE1C;CACH"} \ No newline at end of file diff --git a/build/analytics/player.js b/build/analytics/player.js new file mode 100644 index 00000000..9f2ec785 --- /dev/null +++ b/build/analytics/player.js @@ -0,0 +1,30 @@ +import PlayerAnalyticsModule from './playerAnalyticsModule'; +/** + * Provides the means to control the analytics collected by a `Player`. + * Use the `Player.analytics` property to access a `Player`'s `AnalyticsApi`. + */ +export class AnalyticsApi { + /** + * The native player id that this analytics api is attached to. + */ + playerId; + constructor(playerId) { + this.playerId = playerId; + } + /** + * Sends a sample with the provided custom data. + * Does not change the configured custom data of the collector or source. + */ + sendCustomDataEvent = async (customData) => { + await PlayerAnalyticsModule.sendCustomDataEvent(this.playerId, customData); + }; + /** + * Gets the current user id used by the bundled analytics instance. + * + * @returns The current user id. + */ + getUserId = async () => { + return PlayerAnalyticsModule.getUserId(this.playerId); + }; +} +//# sourceMappingURL=player.js.map \ No newline at end of file diff --git a/build/analytics/player.js.map b/build/analytics/player.js.map new file mode 100644 index 00000000..0a33a0f4 --- /dev/null +++ b/build/analytics/player.js.map @@ -0,0 +1 @@ +{"version":3,"file":"player.js","sourceRoot":"","sources":["../../src/analytics/player.ts"],"names":[],"mappings":"AACA,OAAO,qBAAqB,MAAM,yBAAyB,CAAC;AAE5D;;;GAGG;AACH,MAAM,OAAO,YAAY;IACvB;;OAEG;IACH,QAAQ,CAAS;IAEjB,YAAY,QAAgB;QAC1B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,mBAAmB,GAAG,KAAK,EAAE,UAA4B,EAAE,EAAE;QAC3D,MAAM,qBAAqB,CAAC,mBAAmB,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC7E,CAAC,CAAC;IAEF;;;;OAIG;IACH,SAAS,GAAG,KAAK,IAA4B,EAAE;QAC7C,OAAO,qBAAqB,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxD,CAAC,CAAC;CACH","sourcesContent":["import { CustomDataConfig } from './config';\nimport PlayerAnalyticsModule from './playerAnalyticsModule';\n\n/**\n * Provides the means to control the analytics collected by a `Player`.\n * Use the `Player.analytics` property to access a `Player`'s `AnalyticsApi`.\n */\nexport class AnalyticsApi {\n /**\n * The native player id that this analytics api is attached to.\n */\n playerId: string;\n\n constructor(playerId: string) {\n this.playerId = playerId;\n }\n\n /**\n * Sends a sample with the provided custom data.\n * Does not change the configured custom data of the collector or source.\n */\n sendCustomDataEvent = async (customData: CustomDataConfig) => {\n await PlayerAnalyticsModule.sendCustomDataEvent(this.playerId, customData);\n };\n\n /**\n * Gets the current user id used by the bundled analytics instance.\n *\n * @returns The current user id.\n */\n getUserId = async (): Promise => {\n return PlayerAnalyticsModule.getUserId(this.playerId);\n };\n}\n"]} \ No newline at end of file diff --git a/build/analytics/playerAnalyticsModule.d.ts b/build/analytics/playerAnalyticsModule.d.ts new file mode 100644 index 00000000..fd15e6f2 --- /dev/null +++ b/build/analytics/playerAnalyticsModule.d.ts @@ -0,0 +1,13 @@ +import { NativeModule } from 'expo-modules-core'; +export type PlayerAnalyticsModuleEvents = Record; +/** + * Native PlayerAnalyticsModule using Expo modules API. + * Provides modern async/await interface while maintaining backward compatibility. + */ +declare class PlayerAnalyticsModule extends NativeModule { + sendCustomDataEvent(playerId: string, customData: Record): Promise; + getUserId(playerId: string): Promise; +} +declare const _default: PlayerAnalyticsModule; +export default _default; +//# sourceMappingURL=playerAnalyticsModule.d.ts.map \ No newline at end of file diff --git a/build/analytics/playerAnalyticsModule.d.ts.map b/build/analytics/playerAnalyticsModule.d.ts.map new file mode 100644 index 00000000..4f08f8d9 --- /dev/null +++ b/build/analytics/playerAnalyticsModule.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"playerAnalyticsModule.d.ts","sourceRoot":"","sources":["../../src/analytics/playerAnalyticsModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAEtE,MAAM,MAAM,2BAA2B,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE9D;;;GAGG;AACH,OAAO,OAAO,qBAAsB,SAAQ,YAAY,CAAC,2BAA2B,CAAC;IACnF,mBAAmB,CACjB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC9B,OAAO,CAAC,IAAI,CAAC;IAChB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CACpD;;AAED,wBAEE"} \ No newline at end of file diff --git a/build/analytics/playerAnalyticsModule.js b/build/analytics/playerAnalyticsModule.js new file mode 100644 index 00000000..f6baf94e --- /dev/null +++ b/build/analytics/playerAnalyticsModule.js @@ -0,0 +1,3 @@ +import { requireNativeModule } from 'expo-modules-core'; +export default requireNativeModule('PlayerAnalyticsModule'); +//# sourceMappingURL=playerAnalyticsModule.js.map \ No newline at end of file diff --git a/build/analytics/playerAnalyticsModule.js.map b/build/analytics/playerAnalyticsModule.js.map new file mode 100644 index 00000000..21fa7b6e --- /dev/null +++ b/build/analytics/playerAnalyticsModule.js.map @@ -0,0 +1 @@ +{"version":3,"file":"playerAnalyticsModule.js","sourceRoot":"","sources":["../../src/analytics/playerAnalyticsModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAgBtE,eAAe,mBAAmB,CAChC,uBAAuB,CACxB,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo-modules-core';\n\nexport type PlayerAnalyticsModuleEvents = Record;\n\n/**\n * Native PlayerAnalyticsModule using Expo modules API.\n * Provides modern async/await interface while maintaining backward compatibility.\n */\ndeclare class PlayerAnalyticsModule extends NativeModule {\n sendCustomDataEvent(\n playerId: string,\n customData: Record\n ): Promise;\n getUserId(playerId: string): Promise;\n}\n\nexport default requireNativeModule(\n 'PlayerAnalyticsModule'\n);\n"]} \ No newline at end of file diff --git a/build/audioSession.d.ts b/build/audioSession.d.ts new file mode 100644 index 00000000..8425adc2 --- /dev/null +++ b/build/audioSession.d.ts @@ -0,0 +1,33 @@ +/** + * An audio session category defines a set of audio behaviors. + * Choose a category that most accurately describes the audio behavior you require. + * + * Note the `playback` category is required in order to properly enable picture in picture support. + * + * - `ambient`: The category for an app in which sound playback is nonprimary — that is, your app also works with the sound turned off. + * - `multiRoute`: The category for routing distinct streams of audio data to different output devices at the same time. + * - `playAndRecord`: The category for recording (input) and playback (output) of audio, such as for a Voice over Internet Protocol (VoIP) app. + * - `playback`: The category for playing recorded music or other sounds that are central to the successful use of your app. + * - `record`: The category for recording audio while also silencing playback audio. + * - `soloAmbient`: The default audio session category. + * + * @remarks Platform: iOS + * @see https://developer.apple.com/documentation/avfaudio/avaudiosession/category + */ +export type AudioSessionCategory = 'ambient' | 'multiRoute' | 'playAndRecord' | 'playback' | 'record' | 'soloAmbient'; +/** + * An object that communicates to the system how you intend to use audio in your app. + * + * @remarks Platform: iOS + * @see https://developer.apple.com/documentation/avfaudio/avaudiosession + */ +export declare const AudioSession: { + /** + * Sets the audio session's category. + * + * @remarks Platform: iOS + * @see https://developer.apple.com/documentation/avfaudio/avaudiosession/1616583-setcategory + */ + setCategory: (category: AudioSessionCategory) => Promise; +}; +//# sourceMappingURL=audioSession.d.ts.map \ No newline at end of file diff --git a/build/audioSession.d.ts.map b/build/audioSession.d.ts.map new file mode 100644 index 00000000..9cd53fe2 --- /dev/null +++ b/build/audioSession.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"audioSession.d.ts","sourceRoot":"","sources":["../src/audioSession.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,oBAAoB,GAC5B,SAAS,GACT,YAAY,GACZ,eAAe,GACf,UAAU,GACV,QAAQ,GACR,aAAa,CAAC;AAElB;;;;;GAKG;AACH,eAAO,MAAM,YAAY;IACvB;;;;;OAKG;4BAC2B,oBAAoB,KAAG,OAAO,CAAC,IAAI,CAAC;CAKnE,CAAC"} \ No newline at end of file diff --git a/build/audioSession.js b/build/audioSession.js new file mode 100644 index 00000000..f2d75e26 --- /dev/null +++ b/build/audioSession.js @@ -0,0 +1,21 @@ +import AudioSessionModule from './modules/AudioSessionModule'; +/** + * An object that communicates to the system how you intend to use audio in your app. + * + * @remarks Platform: iOS + * @see https://developer.apple.com/documentation/avfaudio/avaudiosession + */ +export const AudioSession = { + /** + * Sets the audio session's category. + * + * @remarks Platform: iOS + * @see https://developer.apple.com/documentation/avfaudio/avaudiosession/1616583-setcategory + */ + setCategory: async (category) => { + if (AudioSessionModule) { + await AudioSessionModule.setCategory(category); + } + }, +}; +//# sourceMappingURL=audioSession.js.map \ No newline at end of file diff --git a/build/audioSession.js.map b/build/audioSession.js.map new file mode 100644 index 00000000..a099fa20 --- /dev/null +++ b/build/audioSession.js.map @@ -0,0 +1 @@ +{"version":3,"file":"audioSession.js","sourceRoot":"","sources":["../src/audioSession.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,MAAM,8BAA8B,CAAC;AA0B9D;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B;;;;;OAKG;IACH,WAAW,EAAE,KAAK,EAAE,QAA8B,EAAiB,EAAE;QACnE,IAAI,kBAAkB,EAAE,CAAC;YACvB,MAAM,kBAAkB,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;CACF,CAAC","sourcesContent":["import AudioSessionModule from './modules/AudioSessionModule';\n\n/**\n * An audio session category defines a set of audio behaviors.\n * Choose a category that most accurately describes the audio behavior you require.\n *\n * Note the `playback` category is required in order to properly enable picture in picture support.\n *\n * - `ambient`: The category for an app in which sound playback is nonprimary — that is, your app also works with the sound turned off.\n * - `multiRoute`: The category for routing distinct streams of audio data to different output devices at the same time.\n * - `playAndRecord`: The category for recording (input) and playback (output) of audio, such as for a Voice over Internet Protocol (VoIP) app.\n * - `playback`: The category for playing recorded music or other sounds that are central to the successful use of your app.\n * - `record`: The category for recording audio while also silencing playback audio.\n * - `soloAmbient`: The default audio session category.\n *\n * @remarks Platform: iOS\n * @see https://developer.apple.com/documentation/avfaudio/avaudiosession/category\n */\nexport type AudioSessionCategory =\n | 'ambient'\n | 'multiRoute'\n | 'playAndRecord'\n | 'playback'\n | 'record'\n | 'soloAmbient';\n\n/**\n * An object that communicates to the system how you intend to use audio in your app.\n *\n * @remarks Platform: iOS\n * @see https://developer.apple.com/documentation/avfaudio/avaudiosession\n */\nexport const AudioSession = {\n /**\n * Sets the audio session's category.\n *\n * @remarks Platform: iOS\n * @see https://developer.apple.com/documentation/avfaudio/avaudiosession/1616583-setcategory\n */\n setCategory: async (category: AudioSessionCategory): Promise => {\n if (AudioSessionModule) {\n await AudioSessionModule.setCategory(category);\n }\n },\n};\n"]} \ No newline at end of file diff --git a/build/audioTrack.d.ts b/build/audioTrack.d.ts new file mode 100644 index 00000000..802b4f45 --- /dev/null +++ b/build/audioTrack.d.ts @@ -0,0 +1,39 @@ +import { MediaTrackRole } from './mediaTrackRole'; +import { AudioQuality } from './media'; +/** + * Represents an audio track for a video. + */ +export interface AudioTrack { + /** + * The URL to the timed file, e.g. WebVTT file. + */ + url?: string; + /** + * The label for this track. + */ + label?: string; + /** + * The unique identifier for this track. If no value is provided, a random UUIDv4 will be generated for it. + */ + identifier?: string; + /** + * If set to true, this track would be considered as default. Default is `false`. + */ + isDefault?: boolean; + /** + * The IETF BCP 47 language tag associated with this track, e.g. `pt`, `en`, `es` etc. + */ + language?: string; + /** + * An array of {@link MediaTrackRole} objects, each describing a specific role or characteristic of the audio track. + * This property provides a unified way to understand track purposes (e.g., for accessibility) across platforms. + */ + roles?: MediaTrackRole[]; + /** + * The AudioQuality array associated with this AudioTrack. + * + * @platform Android + */ + qualities?: AudioQuality[]; +} +//# sourceMappingURL=audioTrack.d.ts.map \ No newline at end of file diff --git a/build/audioTrack.d.ts.map b/build/audioTrack.d.ts.map new file mode 100644 index 00000000..e0fe12c4 --- /dev/null +++ b/build/audioTrack.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"audioTrack.d.ts","sourceRoot":"","sources":["../src/audioTrack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB;;;;OAIG;IACH,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;CAC5B"} \ No newline at end of file diff --git a/build/audioTrack.js b/build/audioTrack.js new file mode 100644 index 00000000..923ce21e --- /dev/null +++ b/build/audioTrack.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=audioTrack.js.map \ No newline at end of file diff --git a/build/audioTrack.js.map b/build/audioTrack.js.map new file mode 100644 index 00000000..ab2e6121 --- /dev/null +++ b/build/audioTrack.js.map @@ -0,0 +1 @@ +{"version":3,"file":"audioTrack.js","sourceRoot":"","sources":["../src/audioTrack.ts"],"names":[],"mappings":"","sourcesContent":["import { MediaTrackRole } from './mediaTrackRole';\nimport { AudioQuality } from './media';\n\n/**\n * Represents an audio track for a video.\n */\nexport interface AudioTrack {\n /**\n * The URL to the timed file, e.g. WebVTT file.\n */\n url?: string;\n /**\n * The label for this track.\n */\n label?: string;\n /**\n * The unique identifier for this track. If no value is provided, a random UUIDv4 will be generated for it.\n */\n identifier?: string;\n /**\n * If set to true, this track would be considered as default. Default is `false`.\n */\n isDefault?: boolean;\n /**\n * The IETF BCP 47 language tag associated with this track, e.g. `pt`, `en`, `es` etc.\n */\n language?: string;\n /**\n * An array of {@link MediaTrackRole} objects, each describing a specific role or characteristic of the audio track.\n * This property provides a unified way to understand track purposes (e.g., for accessibility) across platforms.\n */\n roles?: MediaTrackRole[];\n /**\n * The AudioQuality array associated with this AudioTrack.\n *\n * @platform Android\n */\n qualities?: AudioQuality[];\n}\n"]} \ No newline at end of file diff --git a/build/bitmovinCastManager.d.ts b/build/bitmovinCastManager.d.ts new file mode 100644 index 00000000..155da13c --- /dev/null +++ b/build/bitmovinCastManager.d.ts @@ -0,0 +1,58 @@ +/** + * The options to be used for initializing `BitmovinCastManager` + * @remarks Platform: Android, iOS + */ +export interface BitmovinCastManagerOptions { + /** + * ID of receiver application. + * Using `null` value will result in using the default application ID + */ + applicationId?: string | null; + /** + * A custom message namespace to be used for communication between sender and receiver. + * Using `null` value will result in using the default message namespace + */ + messageNamespace?: string | null; +} +/** + * Singleton providing access to GoogleCast related features. + * The `BitmovinCastManager` needs to be initialized by calling `BitmovinCastManager.initialize` + * before `Player` creation to enable casting features. + * + * @remarks Platform: Android, iOS + */ +export declare const BitmovinCastManager: { + /** + * Returns whether the `BitmovinCastManager` is initialized. + * @returns A promise that resolves with a boolean indicating whether the `BitmovinCastManager` is initialized + */ + isInitialized: () => Promise; + /** + * Initialize `BitmovinCastManager` based on the provided `BitmovinCastManagerOptions`. + * This method needs to be called before `Player` creation to enable casting features. + * If no options are provided, the default options will be used. + * + * IMPORTANT: This should only be called when the Google Cast SDK is available in the application. + * + * @param options The options to be used for initializing `BitmovinCastManager` + * @returns A promise that resolves when the `BitmovinCastManager` was initialized successfully + */ + initialize: (options?: BitmovinCastManagerOptions | null) => Promise; + /** + * Must be called in every Android Activity to update the context to the current one. + * Make sure to call this method on every Android Activity switch. + * + * @returns A promise that resolves when the context was updated successfully + * @remarks Platform: Android + */ + updateContext: () => Promise; + /** + * Sends the given message to the cast receiver. + * + * @param message The message to be sent + * @param messageNamespace The message namespace to be used, in case of null the default message namespace will be used + * @returns A promise that resolves when the message was sent successfully + */ + sendMessage: (message: string, messageNamespace?: string | null) => Promise; +}; +//# sourceMappingURL=bitmovinCastManager.d.ts.map \ No newline at end of file diff --git a/build/bitmovinCastManager.d.ts.map b/build/bitmovinCastManager.d.ts.map new file mode 100644 index 00000000..1de96902 --- /dev/null +++ b/build/bitmovinCastManager.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"bitmovinCastManager.d.ts","sourceRoot":"","sources":["../src/bitmovinCastManager.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB;IAC9B;;;OAGG;yBACsB,OAAO,CAAC,OAAO,CAAC;IAOzC;;;;;;;;;OASG;2BAEQ,0BAA0B,GAAG,IAAI,KACzC,OAAO,CAAC,IAAI,CAAC;IAShB;;;;;;OAMG;yBACsB,OAAO,CAAC,IAAI,CAAC;IAOtC;;;;;;OAMG;2BACoB,MAAM,qBAAoB,MAAM,GAAG,IAAI;CAS/D,CAAC"} \ No newline at end of file diff --git a/build/bitmovinCastManager.js b/build/bitmovinCastManager.js new file mode 100644 index 00000000..86980041 --- /dev/null +++ b/build/bitmovinCastManager.js @@ -0,0 +1,64 @@ +import { Platform } from 'react-native'; +import BitmovinCastManagerModule from './modules/BitmovinCastManagerModule'; +/** + * Singleton providing access to GoogleCast related features. + * The `BitmovinCastManager` needs to be initialized by calling `BitmovinCastManager.initialize` + * before `Player` creation to enable casting features. + * + * @remarks Platform: Android, iOS + */ +export const BitmovinCastManager = { + /** + * Returns whether the `BitmovinCastManager` is initialized. + * @returns A promise that resolves with a boolean indicating whether the `BitmovinCastManager` is initialized + */ + isInitialized: async () => { + if (Platform.OS === 'ios' && Platform.isTV) { + return false; + } + return BitmovinCastManagerModule.isInitialized(); + }, + /** + * Initialize `BitmovinCastManager` based on the provided `BitmovinCastManagerOptions`. + * This method needs to be called before `Player` creation to enable casting features. + * If no options are provided, the default options will be used. + * + * IMPORTANT: This should only be called when the Google Cast SDK is available in the application. + * + * @param options The options to be used for initializing `BitmovinCastManager` + * @returns A promise that resolves when the `BitmovinCastManager` was initialized successfully + */ + initialize: async (options = null) => { + if (Platform.OS === 'ios' && Platform.isTV) { + return Promise.resolve(); + } + return BitmovinCastManagerModule.initializeCastManager(options || undefined); + }, + /** + * Must be called in every Android Activity to update the context to the current one. + * Make sure to call this method on every Android Activity switch. + * + * @returns A promise that resolves when the context was updated successfully + * @remarks Platform: Android + */ + updateContext: async () => { + if (Platform.OS === 'ios') { + return Promise.resolve(); + } + return BitmovinCastManagerModule.updateContext?.() || Promise.resolve(); + }, + /** + * Sends the given message to the cast receiver. + * + * @param message The message to be sent + * @param messageNamespace The message namespace to be used, in case of null the default message namespace will be used + * @returns A promise that resolves when the message was sent successfully + */ + sendMessage: (message, messageNamespace = null) => { + if (Platform.OS === 'ios' && Platform.isTV) { + return Promise.resolve(); + } + return BitmovinCastManagerModule.sendMessage(message, messageNamespace || undefined); + }, +}; +//# sourceMappingURL=bitmovinCastManager.js.map \ No newline at end of file diff --git a/build/bitmovinCastManager.js.map b/build/bitmovinCastManager.js.map new file mode 100644 index 00000000..c1c23bd4 --- /dev/null +++ b/build/bitmovinCastManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"bitmovinCastManager.js","sourceRoot":"","sources":["../src/bitmovinCastManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,yBAAyB,MAAM,qCAAqC,CAAC;AAmB5E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC;;;OAGG;IACH,aAAa,EAAE,KAAK,IAAsB,EAAE;QAC1C,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;YAC3C,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,yBAAyB,CAAC,aAAa,EAAE,CAAC;IACnD,CAAC;IAED;;;;;;;;;OASG;IACH,UAAU,EAAE,KAAK,EACf,UAA6C,IAAI,EAClC,EAAE;QACjB,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;YAC3C,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QACD,OAAO,yBAAyB,CAAC,qBAAqB,CACpD,OAAO,IAAI,SAAS,CACrB,CAAC;IACJ,CAAC;IAED;;;;;;OAMG;IACH,aAAa,EAAE,KAAK,IAAmB,EAAE;QACvC,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,EAAE,CAAC;YAC1B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QACD,OAAO,yBAAyB,CAAC,aAAa,EAAE,EAAE,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAC1E,CAAC;IAED;;;;;;OAMG;IACH,WAAW,EAAE,CAAC,OAAe,EAAE,mBAAkC,IAAI,EAAE,EAAE;QACvE,IAAI,QAAQ,CAAC,EAAE,KAAK,KAAK,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC;YAC3C,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QACD,OAAO,yBAAyB,CAAC,WAAW,CAC1C,OAAO,EACP,gBAAgB,IAAI,SAAS,CAC9B,CAAC;IACJ,CAAC;CACF,CAAC","sourcesContent":["import { Platform } from 'react-native';\nimport BitmovinCastManagerModule from './modules/BitmovinCastManagerModule';\n\n/**\n * The options to be used for initializing `BitmovinCastManager`\n * @remarks Platform: Android, iOS\n */\nexport interface BitmovinCastManagerOptions {\n /**\n * ID of receiver application.\n * Using `null` value will result in using the default application ID\n */\n applicationId?: string | null;\n /**\n * A custom message namespace to be used for communication between sender and receiver.\n * Using `null` value will result in using the default message namespace\n */\n messageNamespace?: string | null;\n}\n\n/**\n * Singleton providing access to GoogleCast related features.\n * The `BitmovinCastManager` needs to be initialized by calling `BitmovinCastManager.initialize`\n * before `Player` creation to enable casting features.\n *\n * @remarks Platform: Android, iOS\n */\nexport const BitmovinCastManager = {\n /**\n * Returns whether the `BitmovinCastManager` is initialized.\n * @returns A promise that resolves with a boolean indicating whether the `BitmovinCastManager` is initialized\n */\n isInitialized: async (): Promise => {\n if (Platform.OS === 'ios' && Platform.isTV) {\n return false;\n }\n return BitmovinCastManagerModule.isInitialized();\n },\n\n /**\n * Initialize `BitmovinCastManager` based on the provided `BitmovinCastManagerOptions`.\n * This method needs to be called before `Player` creation to enable casting features.\n * If no options are provided, the default options will be used.\n *\n * IMPORTANT: This should only be called when the Google Cast SDK is available in the application.\n *\n * @param options The options to be used for initializing `BitmovinCastManager`\n * @returns A promise that resolves when the `BitmovinCastManager` was initialized successfully\n */\n initialize: async (\n options: BitmovinCastManagerOptions | null = null\n ): Promise => {\n if (Platform.OS === 'ios' && Platform.isTV) {\n return Promise.resolve();\n }\n return BitmovinCastManagerModule.initializeCastManager(\n options || undefined\n );\n },\n\n /**\n * Must be called in every Android Activity to update the context to the current one.\n * Make sure to call this method on every Android Activity switch.\n *\n * @returns A promise that resolves when the context was updated successfully\n * @remarks Platform: Android\n */\n updateContext: async (): Promise => {\n if (Platform.OS === 'ios') {\n return Promise.resolve();\n }\n return BitmovinCastManagerModule.updateContext?.() || Promise.resolve();\n },\n\n /**\n * Sends the given message to the cast receiver.\n *\n * @param message The message to be sent\n * @param messageNamespace The message namespace to be used, in case of null the default message namespace will be used\n * @returns A promise that resolves when the message was sent successfully\n */\n sendMessage: (message: string, messageNamespace: string | null = null) => {\n if (Platform.OS === 'ios' && Platform.isTV) {\n return Promise.resolve();\n }\n return BitmovinCastManagerModule.sendMessage(\n message,\n messageNamespace || undefined\n );\n },\n};\n"]} \ No newline at end of file diff --git a/build/bufferApi.d.ts b/build/bufferApi.d.ts new file mode 100644 index 00000000..43f827e8 --- /dev/null +++ b/build/bufferApi.d.ts @@ -0,0 +1,85 @@ +/** + * Represents different types of media. + */ +export declare enum MediaType { + /** + * Audio media type. + */ + AUDIO = "audio", + /** + * Video media type. + */ + VIDEO = "video" +} +/** + * Represents different types of buffered data. + */ +export declare enum BufferType { + /** + * Represents the buffered data starting at the current playback time. + */ + FORWARD_DURATION = "forwardDuration", + /** + * Represents the buffered data up until the current playback time. + */ + BACKWARD_DURATION = "backwardDuration" +} +/** + * Holds different information about the buffer levels. + */ +export interface BufferLevel { + /** + * The amount of currently buffered data, e.g. audio or video buffer level. + */ + level?: number; + /** + * The target buffer level the player tries to maintain. + */ + targetLevel?: number; + /** + * The media type the buffer data applies to. + */ + media?: MediaType; + /** + * The buffer type the buffer data applies to. + */ + type?: BufferType; +} +/** + * Collection of {@link BufferLevel} objects + */ +export interface BufferLevels { + /** + * {@link BufferLevel} for {@link MediaType.AUDIO}. + */ + audio: BufferLevel; + /** + * {@link BufferLevel} for {@link MediaType.VIDEO}. + */ + video: BufferLevel; +} +/** + * Provides the means to configure buffer settings and to query the current buffer state. + * Accessible through {@link Player.buffer}. + */ +export declare class BufferApi { + /** + * The native player id that this buffer api is attached to. + */ + readonly nativeId: string; + constructor(playerId: string); + /** + * Gets the {@link BufferLevel|buffer level} from the Player + * @param type The {@link BufferType} to return the level for. + * @returns a {@link BufferLevels} that contains {@link BufferLevel} values for audio and video. + */ + getLevel: (type: BufferType) => Promise; + /** + * Sets the target buffer level for the chosen buffer {@link BufferType} across all {@link MediaType} options. + * + * @param type The {@link BufferType} to set the target level for. On iOS and tvOS, only {@link BufferType.FORWARD_DURATION} is supported. + * @param value The value to set. On iOS and tvOS when passing `0`, the player will choose an appropriate forward buffer duration suitable for most use-cases. On Android setting to `0` will have no effect. + */ + setTargetLevel: (type: BufferType, value: number) => Promise; +} +//# sourceMappingURL=bufferApi.d.ts.map \ No newline at end of file diff --git a/build/bufferApi.d.ts.map b/build/bufferApi.d.ts.map new file mode 100644 index 00000000..1c8755c5 --- /dev/null +++ b/build/bufferApi.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"bufferApi.d.ts","sourceRoot":"","sources":["../src/bufferApi.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,oBAAY,SAAS;IACnB;;OAEG;IACH,KAAK,UAAU;IACf;;OAEG;IACH,KAAK,UAAU;CAChB;AAED;;GAEG;AACH,oBAAY,UAAU;IACpB;;OAEG;IACH,gBAAgB,oBAAoB;IACpC;;OAEG;IACH,iBAAiB,qBAAqB;CACvC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;OAEG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,KAAK,EAAE,WAAW,CAAC;IACnB;;OAEG;IACH,KAAK,EAAE,WAAW,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,SAAS;IACpB;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,QAAQ,EAAE,MAAM;IAI5B;;;;OAIG;IACH,QAAQ,GAAU,MAAM,UAAU,KAAG,OAAO,CAAC,YAAY,CAAC,CAExD;IAEF;;;;;OAKG;IACH,cAAc,GAAU,MAAM,UAAU,EAAE,OAAO,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAErE;CACH"} \ No newline at end of file diff --git a/build/bufferApi.js b/build/bufferApi.js new file mode 100644 index 00000000..d822117a --- /dev/null +++ b/build/bufferApi.js @@ -0,0 +1,60 @@ +import BufferModule from './modules/BufferModule'; +/** + * Represents different types of media. + */ +export var MediaType; +(function (MediaType) { + /** + * Audio media type. + */ + MediaType["AUDIO"] = "audio"; + /** + * Video media type. + */ + MediaType["VIDEO"] = "video"; +})(MediaType || (MediaType = {})); +/** + * Represents different types of buffered data. + */ +export var BufferType; +(function (BufferType) { + /** + * Represents the buffered data starting at the current playback time. + */ + BufferType["FORWARD_DURATION"] = "forwardDuration"; + /** + * Represents the buffered data up until the current playback time. + */ + BufferType["BACKWARD_DURATION"] = "backwardDuration"; +})(BufferType || (BufferType = {})); +/** + * Provides the means to configure buffer settings and to query the current buffer state. + * Accessible through {@link Player.buffer}. + */ +export class BufferApi { + /** + * The native player id that this buffer api is attached to. + */ + nativeId; + constructor(playerId) { + this.nativeId = playerId; + } + /** + * Gets the {@link BufferLevel|buffer level} from the Player + * @param type The {@link BufferType} to return the level for. + * @returns a {@link BufferLevels} that contains {@link BufferLevel} values for audio and video. + */ + getLevel = async (type) => { + return BufferModule.getLevel(this.nativeId, type); + }; + /** + * Sets the target buffer level for the chosen buffer {@link BufferType} across all {@link MediaType} options. + * + * @param type The {@link BufferType} to set the target level for. On iOS and tvOS, only {@link BufferType.FORWARD_DURATION} is supported. + * @param value The value to set. On iOS and tvOS when passing `0`, the player will choose an appropriate forward buffer duration suitable for most use-cases. On Android setting to `0` will have no effect. + */ + setTargetLevel = async (type, value) => { + return BufferModule.setTargetLevel(this.nativeId, type, value); + }; +} +//# sourceMappingURL=bufferApi.js.map \ No newline at end of file diff --git a/build/bufferApi.js.map b/build/bufferApi.js.map new file mode 100644 index 00000000..f19bf427 --- /dev/null +++ b/build/bufferApi.js.map @@ -0,0 +1 @@ +{"version":3,"file":"bufferApi.js","sourceRoot":"","sources":["../src/bufferApi.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,wBAAwB,CAAC;AAElD;;GAEG;AACH,MAAM,CAAN,IAAY,SASX;AATD,WAAY,SAAS;IACnB;;OAEG;IACH,4BAAe,CAAA;IACf;;OAEG;IACH,4BAAe,CAAA;AACjB,CAAC,EATW,SAAS,KAAT,SAAS,QASpB;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,UASX;AATD,WAAY,UAAU;IACpB;;OAEG;IACH,kDAAoC,CAAA;IACpC;;OAEG;IACH,oDAAsC,CAAA;AACxC,CAAC,EATW,UAAU,KAAV,UAAU,QASrB;AAsCD;;;GAGG;AACH,MAAM,OAAO,SAAS;IACpB;;OAEG;IACM,QAAQ,CAAS;IAE1B,YAAY,QAAgB;QAC1B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED;;;;OAIG;IACH,QAAQ,GAAG,KAAK,EAAE,IAAgB,EAAyB,EAAE;QAC3D,OAAO,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACpD,CAAC,CAAC;IAEF;;;;;OAKG;IACH,cAAc,GAAG,KAAK,EAAE,IAAgB,EAAE,KAAa,EAAiB,EAAE;QACxE,OAAO,YAAY,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IACjE,CAAC,CAAC;CACH","sourcesContent":["import BufferModule from './modules/BufferModule';\n\n/**\n * Represents different types of media.\n */\nexport enum MediaType {\n /**\n * Audio media type.\n */\n AUDIO = 'audio',\n /**\n * Video media type.\n */\n VIDEO = 'video',\n}\n\n/**\n * Represents different types of buffered data.\n */\nexport enum BufferType {\n /**\n * Represents the buffered data starting at the current playback time.\n */\n FORWARD_DURATION = 'forwardDuration',\n /**\n * Represents the buffered data up until the current playback time.\n */\n BACKWARD_DURATION = 'backwardDuration',\n}\n\n/**\n * Holds different information about the buffer levels.\n */\nexport interface BufferLevel {\n /**\n * The amount of currently buffered data, e.g. audio or video buffer level.\n */\n level?: number;\n /**\n * The target buffer level the player tries to maintain.\n */\n targetLevel?: number;\n /**\n * The media type the buffer data applies to.\n */\n media?: MediaType;\n /**\n * The buffer type the buffer data applies to.\n */\n type?: BufferType;\n}\n\n/**\n * Collection of {@link BufferLevel} objects\n */\nexport interface BufferLevels {\n /**\n * {@link BufferLevel} for {@link MediaType.AUDIO}.\n */\n audio: BufferLevel;\n /**\n * {@link BufferLevel} for {@link MediaType.VIDEO}.\n */\n video: BufferLevel;\n}\n\n/**\n * Provides the means to configure buffer settings and to query the current buffer state.\n * Accessible through {@link Player.buffer}.\n */\nexport class BufferApi {\n /**\n * The native player id that this buffer api is attached to.\n */\n readonly nativeId: string;\n\n constructor(playerId: string) {\n this.nativeId = playerId;\n }\n\n /**\n * Gets the {@link BufferLevel|buffer level} from the Player\n * @param type The {@link BufferType} to return the level for.\n * @returns a {@link BufferLevels} that contains {@link BufferLevel} values for audio and video.\n */\n getLevel = async (type: BufferType): Promise => {\n return BufferModule.getLevel(this.nativeId, type);\n };\n\n /**\n * Sets the target buffer level for the chosen buffer {@link BufferType} across all {@link MediaType} options.\n *\n * @param type The {@link BufferType} to set the target level for. On iOS and tvOS, only {@link BufferType.FORWARD_DURATION} is supported.\n * @param value The value to set. On iOS and tvOS when passing `0`, the player will choose an appropriate forward buffer duration suitable for most use-cases. On Android setting to `0` will have no effect.\n */\n setTargetLevel = async (type: BufferType, value: number): Promise => {\n return BufferModule.setTargetLevel(this.nativeId, type, value);\n };\n}\n"]} \ No newline at end of file diff --git a/build/bufferConfig.d.ts b/build/bufferConfig.d.ts new file mode 100644 index 00000000..10778348 --- /dev/null +++ b/build/bufferConfig.d.ts @@ -0,0 +1,42 @@ +/** + * Configures buffer target levels for different MediaTypes. + */ +export interface BufferMediaTypeConfig { + /** + * The amount of data in seconds the player tries to buffer in advance. + * + * iOS and tvOS, only: If set to `0`, the player will choose an appropriate forward buffer duration suitable + * for most use-cases. + * + * Default value is `0` on iOS and tvOS, `50` on Android + */ + forwardDuration?: number; +} +/** + * Player buffer config object to configure buffering behavior. + */ +export interface BufferConfig { + /** + * Configures various settings for the audio and video buffer. + */ + audioAndVideo?: BufferMediaTypeConfig; + /** + * Amount of seconds the player buffers before playback starts again after a stall. This value is + * restricted to the maximum value of the buffer minus 0.5 seconds. + * + * Default is `5` seconds. + * + * @remarks Platform: Android + */ + restartThreshold?: number; + /** + * Amount of seconds the player buffers before playback starts. This value is restricted to the + * maximum value of the buffer minus 0.5 seconds. + * + * Default is `2.5` seconds. + * + * @remarks Platform: Android + */ + startupThreshold?: number; +} +//# sourceMappingURL=bufferConfig.d.ts.map \ No newline at end of file diff --git a/build/bufferConfig.d.ts.map b/build/bufferConfig.d.ts.map new file mode 100644 index 00000000..68710596 --- /dev/null +++ b/build/bufferConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"bufferConfig.d.ts","sourceRoot":"","sources":["../src/bufferConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,aAAa,CAAC,EAAE,qBAAqB,CAAC;IACtC;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B"} \ No newline at end of file diff --git a/build/bufferConfig.js b/build/bufferConfig.js new file mode 100644 index 00000000..8016d07e --- /dev/null +++ b/build/bufferConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=bufferConfig.js.map \ No newline at end of file diff --git a/build/bufferConfig.js.map b/build/bufferConfig.js.map new file mode 100644 index 00000000..27629e37 --- /dev/null +++ b/build/bufferConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"bufferConfig.js","sourceRoot":"","sources":["../src/bufferConfig.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Configures buffer target levels for different MediaTypes.\n */\nexport interface BufferMediaTypeConfig {\n /**\n * The amount of data in seconds the player tries to buffer in advance.\n *\n * iOS and tvOS, only: If set to `0`, the player will choose an appropriate forward buffer duration suitable\n * for most use-cases.\n *\n * Default value is `0` on iOS and tvOS, `50` on Android\n */\n forwardDuration?: number;\n}\n\n/**\n * Player buffer config object to configure buffering behavior.\n */\nexport interface BufferConfig {\n /**\n * Configures various settings for the audio and video buffer.\n */\n audioAndVideo?: BufferMediaTypeConfig;\n /**\n * Amount of seconds the player buffers before playback starts again after a stall. This value is\n * restricted to the maximum value of the buffer minus 0.5 seconds.\n *\n * Default is `5` seconds.\n *\n * @remarks Platform: Android\n */\n restartThreshold?: number;\n /**\n * Amount of seconds the player buffers before playback starts. This value is restricted to the\n * maximum value of the buffer minus 0.5 seconds.\n *\n * Default is `2.5` seconds.\n *\n * @remarks Platform: Android\n */\n startupThreshold?: number;\n}\n"]} \ No newline at end of file diff --git a/build/components/PlayerView/events.d.ts b/build/components/PlayerView/events.d.ts new file mode 100644 index 00000000..840bc61c --- /dev/null +++ b/build/components/PlayerView/events.d.ts @@ -0,0 +1,310 @@ +import { AdBreakFinishedEvent, AdBreakStartedEvent, AdClickedEvent, AdErrorEvent, AdFinishedEvent, AdManifestLoadedEvent, AdManifestLoadEvent, AdQuartileEvent, AdScheduledEvent, AdSkippedEvent, AdStartedEvent, CastAvailableEvent, CastPausedEvent, CastPlaybackFinishedEvent, CastPlayingEvent, CastStartedEvent, CastStartEvent, CastStoppedEvent, CastTimeUpdatedEvent, CastWaitingForDeviceEvent, DestroyEvent, Event, FullscreenEnabledEvent, FullscreenDisabledEvent, FullscreenEnterEvent, FullscreenExitEvent, MutedEvent, PausedEvent, PictureInPictureAvailabilityChangedEvent, PictureInPictureEnterEvent, PictureInPictureEnteredEvent, PictureInPictureExitEvent, PictureInPictureExitedEvent, PlaybackFinishedEvent, PlayerActiveEvent, PlayerErrorEvent, PlayerWarningEvent, PlayEvent, PlayingEvent, ReadyEvent, SeekedEvent, SeekEvent, TimeShiftEvent, TimeShiftedEvent, StallStartedEvent, StallEndedEvent, SourceErrorEvent, SourceLoadedEvent, SourceLoadEvent, SourceUnloadedEvent, SourceWarningEvent, AudioAddedEvent, AudioChangedEvent, AudioRemovedEvent, SubtitleAddedEvent, SubtitleChangedEvent, SubtitleRemovedEvent, TimeChangedEvent, UnmutedEvent, VideoPlaybackQualityChangedEvent, DownloadFinishedEvent, VideoDownloadQualityChangedEvent, PlaybackSpeedChangedEvent, CueEnterEvent, CueExitEvent } from '../../events'; +/** + * Event props for `PlayerView`. + * + * Note the events of `PlayerView` are simply a proxy over + * the events from `NativePlayerView` just removing RN's bubbling data. + */ +export type PlayerViewEvents = { + /** + * Event emitted when an ad break has finished. + */ + onAdBreakFinished?: (event: AdBreakFinishedEvent) => void; + /** + * Event emitted when an ad break has started. + */ + onAdBreakStarted?: (event: AdBreakStartedEvent) => void; + /** + * Event emitted when an ad has been clicked. + */ + onAdClicked?: (event: AdClickedEvent) => void; + /** + * Event emitted when an ad error has occurred. + */ + onAdError?: (event: AdErrorEvent) => void; + /** + * Event emitted when an ad has finished. + */ + onAdFinished?: (event: AdFinishedEvent) => void; + /** + * Event emitted when an ad manifest starts loading. + */ + onAdManifestLoad?: (event: AdManifestLoadEvent) => void; + /** + * Event emitted when an ad manifest has been loaded. + */ + onAdManifestLoaded?: (event: AdManifestLoadedEvent) => void; + /** + * Event emitted when an ad quartile has been reached. + */ + onAdQuartile?: (event: AdQuartileEvent) => void; + /** + * Event emitted when an ad has been scheduled. + */ + onAdScheduled?: (event: AdScheduledEvent) => void; + /** + * Event emitted when an ad has been skipped. + */ + onAdSkipped?: (event: AdSkippedEvent) => void; + /** + * Event emitted when an ad has started. + */ + onAdStarted?: (event: AdStartedEvent) => void; + /** + * Event emitted when casting to a cast-compatible device is available. + * + * @remarks Platform: iOS, Android + */ + onCastAvailable?: (event: CastAvailableEvent) => void; + /** + * Event emitted when the playback on a cast-compatible device was paused. + * + * @remarks Platform: iOS, Android + */ + onCastPaused?: (event: CastPausedEvent) => void; + /** + * Event emitted when the playback on a cast-compatible device has finished. + * + * @remarks Platform: iOS, Android + */ + onCastPlaybackFinished?: (event: CastPlaybackFinishedEvent) => void; + /** + * Event emitted when playback on a cast-compatible device has started. + * + * @remarks Platform: iOS, Android + */ + onCastPlaying?: (event: CastPlayingEvent) => void; + /** + * Event emitted when the cast app is launched successfully. + * + * @remarks Platform: iOS, Android + */ + onCastStarted?: (event: CastStartedEvent) => void; + /** + * Event emitted when casting is initiated, but the user still needs to choose which device should be used. + * + * @remarks Platform: iOS, Android + */ + onCastStart?: (event: CastStartEvent) => void; + /** + * Event emitted when casting to a cast-compatible device is stopped. + * + * @remarks Platform: iOS, Android + */ + onCastStopped?: (event: CastStoppedEvent) => void; + /** + * Event emitted when the time update from the currently used cast-compatible device is received. + * + * @remarks Platform: iOS, Android + */ + onCastTimeUpdated?: (event: CastTimeUpdatedEvent) => void; + /** + * Event emitted when a cast-compatible device has been chosen and the player is waiting for the device to get ready for + * playback. + * + * @remarks Platform: iOS, Android + */ + onCastWaitingForDevice?: (event: CastWaitingForDeviceEvent) => void; + /** + * Event emitted when a subtitle entry transitions into the active status. + */ + onCueEnter?: (event: CueEnterEvent) => void; + /** + * Event emitted when an active subtitle entry transitions into the inactive status. + */ + onCueExit?: (event: CueExitEvent) => void; + /** + * Event emitted when the player is destroyed. + */ + onDestroy?: (event: DestroyEvent) => void; + /** + * Emitted when a download was finished. + */ + onDownloadFinished?: (event: DownloadFinishedEvent) => void; + /** + * All events emitted by the player. + */ + onEvent?: (event: Event) => void; + /** + * Event emitted when fullscreen mode has been enabled. + * + * @remarks Platform: iOS, Android + */ + onFullscreenEnabled?: (event: FullscreenEnabledEvent) => void; + /** + * Event emitted when fullscreen mode has been disabled. + * + * @remarks Platform: iOS, Android + */ + onFullscreenDisabled?: (event: FullscreenDisabledEvent) => void; + /** + * Event emitted when fullscreen mode has been entered. + * + * @remarks Platform: iOS, Android + */ + onFullscreenEnter?: (event: FullscreenEnterEvent) => void; + /** + * Event emitted when fullscreen mode has been exited. + * + * @remarks Platform: iOS, Android + */ + onFullscreenExit?: (event: FullscreenExitEvent) => void; + /** + * Event emitted when the player has been muted. + */ + onMuted?: (event: MutedEvent) => void; + /** + * Event emitted when the player has been paused. + */ + onPaused?: (event: PausedEvent) => void; + /** + * Event mitted when the availability of the Picture in Picture mode changed. + */ + onPictureInPictureAvailabilityChanged?: (event: PictureInPictureAvailabilityChangedEvent) => void; + /** + * Event emitted when the player enters Picture in Picture mode. + */ + onPictureInPictureEnter?: (event: PictureInPictureEnterEvent) => void; + /** + * Event emitted when the player entered Picture in Picture mode. + * + * @remarks Platform: iOS + */ + onPictureInPictureEntered?: (event: PictureInPictureEnteredEvent) => void; + /** + * Event emitted when the player exits Picture in Picture mode. + */ + onPictureInPictureExit?: (event: PictureInPictureExitEvent) => void; + /** + * Event emitted when the player exited Picture in Picture mode. + * + * @remarks Platform: iOS + */ + onPictureInPictureExited?: (event: PictureInPictureExitedEvent) => void; + /** + * Event emitted when the player received an intention to start/resume playback. + */ + onPlay?: (event: PlayEvent) => void; + /** + * Event emitted when the playback of the current media has finished. + */ + onPlaybackFinished?: (event: PlaybackFinishedEvent) => void; + /** + * Emitted when the player transitions from one playback speed to another. + * @remarks Platform: iOS, tvOS + */ + onPlaybackSpeedChanged?: (event: PlaybackSpeedChangedEvent) => void; + /** + * Event emitted when a source is loaded into the player. + * Seeking and time shifting are allowed as soon as this event is seen. + */ + onPlayerActive?: (event: PlayerActiveEvent) => void; + /** + * Event Emitted when a player error occurred. + */ + onPlayerError?: (event: PlayerErrorEvent) => void; + /** + * Event emitted when a player warning occurred. + */ + onPlayerWarning?: (event: PlayerWarningEvent) => void; + /** + * Emitted when playback has started. + */ + onPlaying?: (event: PlayingEvent) => void; + /** + * Emitted when the player is ready for immediate playback, because initial audio/video + * has been downloaded. + */ + onReady?: (event: ReadyEvent) => void; + /** + * Event emitted when the player is about to seek to a new position. + * Only applies to VoD streams. + */ + onSeek?: (event: SeekEvent) => void; + /** + * Event emitted when seeking has finished and data to continue playback is available. + * Only applies to VoD streams. + */ + onSeeked?: (event: SeekedEvent) => void; + /** + * Event mitted when the player starts time shifting. + * Only applies to live streams. + */ + onTimeShift?: (event: TimeShiftEvent) => void; + /** + * Event emitted when time shifting has finished and data is available to continue playback. + * Only applies to live streams. + */ + onTimeShifted?: (event: TimeShiftedEvent) => void; + /** + * Event emitted when the player begins to stall and to buffer due to an empty buffer. + */ + onStallStarted?: (event: StallStartedEvent) => void; + /** + * Event emitted when the player ends stalling, due to enough data in the buffer. + */ + onStallEnded?: (event: StallEndedEvent) => void; + /** + * Event emitted when a source error occurred. + */ + onSourceError?: (event: SourceErrorEvent) => void; + /** + * Event emitted when a new source loading has started. + */ + onSourceLoad?: (event: SourceLoadEvent) => void; + /** + * Event emitted when a new source is loaded. + * This does not mean that the source is immediately ready for playback. + * `ReadyEvent` indicates the player is ready for immediate playback. + */ + onSourceLoaded?: (event: SourceLoadedEvent) => void; + /** + * Event emitted when the current source has been unloaded. + */ + onSourceUnloaded?: (event: SourceUnloadedEvent) => void; + /** + * Event emitted when a source warning occurred. + */ + onSourceWarning?: (event: SourceWarningEvent) => void; + /** + * Event emitted when a new audio track is added to the player. + */ + onAudioAdded?: (event: AudioAddedEvent) => void; + /** + * Event emitted when the player's selected audio track has changed. + */ + onAudioChanged?: (event: AudioChangedEvent) => void; + /** + * Event emitted when an audio track is removed from the player. + */ + onAudioRemoved?: (event: AudioRemovedEvent) => void; + /** + * Event emitted when a new subtitle track is added to the player. + */ + onSubtitleAdded?: (event: SubtitleAddedEvent) => void; + /** + * Event emitted when the player's selected subtitle track has changed. + */ + onSubtitleChanged?: (event: SubtitleChangedEvent) => void; + /** + * Event emitted when a subtitle track is removed from the player. + */ + onSubtitleRemoved?: (event: SubtitleRemovedEvent) => void; + /** + * Event emitted when the current playback time has changed. + */ + onTimeChanged?: (event: TimeChangedEvent) => void; + /** + * Emitted when the player is unmuted. + */ + onUnmuted?: (event: UnmutedEvent) => void; + /** + * Emitted when current video download quality has changed. + */ + onVideoDownloadQualityChanged?: (event: VideoDownloadQualityChangedEvent) => void; + /** + * Emitted when the current video playback quality has changed. + */ + onVideoPlaybackQualityChanged?: (event: VideoPlaybackQualityChangedEvent) => void; +}; +//# sourceMappingURL=events.d.ts.map \ No newline at end of file diff --git a/build/components/PlayerView/events.d.ts.map b/build/components/PlayerView/events.d.ts.map new file mode 100644 index 00000000..dc5163e3 --- /dev/null +++ b/build/components/PlayerView/events.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../src/components/PlayerView/events.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/build/components/PlayerView/events.js b/build/components/PlayerView/events.js new file mode 100644 index 00000000..4b09bff3 --- /dev/null +++ b/build/components/PlayerView/events.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=events.js.map \ No newline at end of file diff --git a/build/components/PlayerView/events.js.map b/build/components/PlayerView/events.js.map new file mode 100644 index 00000000..87fc13ff --- /dev/null +++ b/build/components/PlayerView/events.js.map @@ -0,0 +1 @@ +{"version":3,"file":"events.js","sourceRoot":"","sources":["../../../src/components/PlayerView/events.ts"],"names":[],"mappings":"","sourcesContent":["import {\n AdBreakFinishedEvent,\n AdBreakStartedEvent,\n AdClickedEvent,\n AdErrorEvent,\n AdFinishedEvent,\n AdManifestLoadedEvent,\n AdManifestLoadEvent,\n AdQuartileEvent,\n AdScheduledEvent,\n AdSkippedEvent,\n AdStartedEvent,\n CastAvailableEvent,\n CastPausedEvent,\n CastPlaybackFinishedEvent,\n CastPlayingEvent,\n CastStartedEvent,\n CastStartEvent,\n CastStoppedEvent,\n CastTimeUpdatedEvent,\n CastWaitingForDeviceEvent,\n DestroyEvent,\n Event,\n FullscreenEnabledEvent,\n FullscreenDisabledEvent,\n FullscreenEnterEvent,\n FullscreenExitEvent,\n MutedEvent,\n PausedEvent,\n PictureInPictureAvailabilityChangedEvent,\n PictureInPictureEnterEvent,\n PictureInPictureEnteredEvent,\n PictureInPictureExitEvent,\n PictureInPictureExitedEvent,\n PlaybackFinishedEvent,\n PlayerActiveEvent,\n PlayerErrorEvent,\n PlayerWarningEvent,\n PlayEvent,\n PlayingEvent,\n ReadyEvent,\n SeekedEvent,\n SeekEvent,\n TimeShiftEvent,\n TimeShiftedEvent,\n StallStartedEvent,\n StallEndedEvent,\n SourceErrorEvent,\n SourceLoadedEvent,\n SourceLoadEvent,\n SourceUnloadedEvent,\n SourceWarningEvent,\n AudioAddedEvent,\n AudioChangedEvent,\n AudioRemovedEvent,\n SubtitleAddedEvent,\n SubtitleChangedEvent,\n SubtitleRemovedEvent,\n TimeChangedEvent,\n UnmutedEvent,\n VideoPlaybackQualityChangedEvent,\n DownloadFinishedEvent,\n VideoDownloadQualityChangedEvent,\n PlaybackSpeedChangedEvent,\n CueEnterEvent,\n CueExitEvent,\n} from '../../events';\n\n/**\n * Event props for `PlayerView`.\n *\n * Note the events of `PlayerView` are simply a proxy over\n * the events from `NativePlayerView` just removing RN's bubbling data.\n */\nexport type PlayerViewEvents = {\n /**\n * Event emitted when an ad break has finished.\n */\n onAdBreakFinished?: (event: AdBreakFinishedEvent) => void;\n /**\n * Event emitted when an ad break has started.\n */\n onAdBreakStarted?: (event: AdBreakStartedEvent) => void;\n /**\n * Event emitted when an ad has been clicked.\n */\n onAdClicked?: (event: AdClickedEvent) => void;\n /**\n * Event emitted when an ad error has occurred.\n */\n onAdError?: (event: AdErrorEvent) => void;\n /**\n * Event emitted when an ad has finished.\n */\n onAdFinished?: (event: AdFinishedEvent) => void;\n /**\n * Event emitted when an ad manifest starts loading.\n */\n onAdManifestLoad?: (event: AdManifestLoadEvent) => void;\n /**\n * Event emitted when an ad manifest has been loaded.\n */\n onAdManifestLoaded?: (event: AdManifestLoadedEvent) => void;\n /**\n * Event emitted when an ad quartile has been reached.\n */\n onAdQuartile?: (event: AdQuartileEvent) => void;\n /**\n * Event emitted when an ad has been scheduled.\n */\n onAdScheduled?: (event: AdScheduledEvent) => void;\n /**\n * Event emitted when an ad has been skipped.\n */\n onAdSkipped?: (event: AdSkippedEvent) => void;\n /**\n * Event emitted when an ad has started.\n */\n onAdStarted?: (event: AdStartedEvent) => void;\n /**\n * Event emitted when casting to a cast-compatible device is available.\n *\n * @remarks Platform: iOS, Android\n */\n onCastAvailable?: (event: CastAvailableEvent) => void;\n /**\n * Event emitted when the playback on a cast-compatible device was paused.\n *\n * @remarks Platform: iOS, Android\n */\n onCastPaused?: (event: CastPausedEvent) => void;\n /**\n * Event emitted when the playback on a cast-compatible device has finished.\n *\n * @remarks Platform: iOS, Android\n */\n onCastPlaybackFinished?: (event: CastPlaybackFinishedEvent) => void;\n /**\n * Event emitted when playback on a cast-compatible device has started.\n *\n * @remarks Platform: iOS, Android\n */\n onCastPlaying?: (event: CastPlayingEvent) => void;\n /**\n * Event emitted when the cast app is launched successfully.\n *\n * @remarks Platform: iOS, Android\n */\n onCastStarted?: (event: CastStartedEvent) => void;\n /**\n * Event emitted when casting is initiated, but the user still needs to choose which device should be used.\n *\n * @remarks Platform: iOS, Android\n */\n onCastStart?: (event: CastStartEvent) => void;\n /**\n * Event emitted when casting to a cast-compatible device is stopped.\n *\n * @remarks Platform: iOS, Android\n */\n onCastStopped?: (event: CastStoppedEvent) => void;\n /**\n * Event emitted when the time update from the currently used cast-compatible device is received.\n *\n * @remarks Platform: iOS, Android\n */\n onCastTimeUpdated?: (event: CastTimeUpdatedEvent) => void;\n /**\n * Event emitted when a cast-compatible device has been chosen and the player is waiting for the device to get ready for\n * playback.\n *\n * @remarks Platform: iOS, Android\n */\n onCastWaitingForDevice?: (event: CastWaitingForDeviceEvent) => void;\n /**\n * Event emitted when a subtitle entry transitions into the active status.\n */\n onCueEnter?: (event: CueEnterEvent) => void;\n /**\n * Event emitted when an active subtitle entry transitions into the inactive status.\n */\n onCueExit?: (event: CueExitEvent) => void;\n /**\n * Event emitted when the player is destroyed.\n */\n onDestroy?: (event: DestroyEvent) => void;\n /**\n * Emitted when a download was finished.\n */\n onDownloadFinished?: (event: DownloadFinishedEvent) => void;\n /**\n * All events emitted by the player.\n */\n onEvent?: (event: Event) => void;\n /**\n * Event emitted when fullscreen mode has been enabled.\n *\n * @remarks Platform: iOS, Android\n */\n onFullscreenEnabled?: (event: FullscreenEnabledEvent) => void;\n /**\n * Event emitted when fullscreen mode has been disabled.\n *\n * @remarks Platform: iOS, Android\n */\n onFullscreenDisabled?: (event: FullscreenDisabledEvent) => void;\n /**\n * Event emitted when fullscreen mode has been entered.\n *\n * @remarks Platform: iOS, Android\n */\n onFullscreenEnter?: (event: FullscreenEnterEvent) => void;\n /**\n * Event emitted when fullscreen mode has been exited.\n *\n * @remarks Platform: iOS, Android\n */\n onFullscreenExit?: (event: FullscreenExitEvent) => void;\n /**\n * Event emitted when the player has been muted.\n */\n onMuted?: (event: MutedEvent) => void;\n /**\n * Event emitted when the player has been paused.\n */\n onPaused?: (event: PausedEvent) => void;\n /**\n * Event mitted when the availability of the Picture in Picture mode changed.\n */\n onPictureInPictureAvailabilityChanged?: (\n event: PictureInPictureAvailabilityChangedEvent\n ) => void;\n /**\n * Event emitted when the player enters Picture in Picture mode.\n */\n onPictureInPictureEnter?: (event: PictureInPictureEnterEvent) => void;\n /**\n * Event emitted when the player entered Picture in Picture mode.\n *\n * @remarks Platform: iOS\n */\n onPictureInPictureEntered?: (event: PictureInPictureEnteredEvent) => void;\n /**\n * Event emitted when the player exits Picture in Picture mode.\n */\n onPictureInPictureExit?: (event: PictureInPictureExitEvent) => void;\n /**\n * Event emitted when the player exited Picture in Picture mode.\n *\n * @remarks Platform: iOS\n */\n onPictureInPictureExited?: (event: PictureInPictureExitedEvent) => void;\n /**\n * Event emitted when the player received an intention to start/resume playback.\n */\n onPlay?: (event: PlayEvent) => void;\n /**\n * Event emitted when the playback of the current media has finished.\n */\n onPlaybackFinished?: (event: PlaybackFinishedEvent) => void;\n /**\n * Emitted when the player transitions from one playback speed to another.\n * @remarks Platform: iOS, tvOS\n */\n onPlaybackSpeedChanged?: (event: PlaybackSpeedChangedEvent) => void;\n /**\n * Event emitted when a source is loaded into the player.\n * Seeking and time shifting are allowed as soon as this event is seen.\n */\n onPlayerActive?: (event: PlayerActiveEvent) => void;\n /**\n * Event Emitted when a player error occurred.\n */\n onPlayerError?: (event: PlayerErrorEvent) => void;\n /**\n * Event emitted when a player warning occurred.\n */\n onPlayerWarning?: (event: PlayerWarningEvent) => void;\n /**\n * Emitted when playback has started.\n */\n onPlaying?: (event: PlayingEvent) => void;\n /**\n * Emitted when the player is ready for immediate playback, because initial audio/video\n * has been downloaded.\n */\n onReady?: (event: ReadyEvent) => void;\n /**\n * Event emitted when the player is about to seek to a new position.\n * Only applies to VoD streams.\n */\n onSeek?: (event: SeekEvent) => void;\n /**\n * Event emitted when seeking has finished and data to continue playback is available.\n * Only applies to VoD streams.\n */\n onSeeked?: (event: SeekedEvent) => void;\n /**\n * Event mitted when the player starts time shifting.\n * Only applies to live streams.\n */\n onTimeShift?: (event: TimeShiftEvent) => void;\n /**\n * Event emitted when time shifting has finished and data is available to continue playback.\n * Only applies to live streams.\n */\n onTimeShifted?: (event: TimeShiftedEvent) => void;\n /**\n * Event emitted when the player begins to stall and to buffer due to an empty buffer.\n */\n onStallStarted?: (event: StallStartedEvent) => void;\n /**\n * Event emitted when the player ends stalling, due to enough data in the buffer.\n */\n onStallEnded?: (event: StallEndedEvent) => void;\n /**\n * Event emitted when a source error occurred.\n */\n onSourceError?: (event: SourceErrorEvent) => void;\n /**\n * Event emitted when a new source loading has started.\n */\n onSourceLoad?: (event: SourceLoadEvent) => void;\n /**\n * Event emitted when a new source is loaded.\n * This does not mean that the source is immediately ready for playback.\n * `ReadyEvent` indicates the player is ready for immediate playback.\n */\n onSourceLoaded?: (event: SourceLoadedEvent) => void;\n /**\n * Event emitted when the current source has been unloaded.\n */\n onSourceUnloaded?: (event: SourceUnloadedEvent) => void;\n /**\n * Event emitted when a source warning occurred.\n */\n onSourceWarning?: (event: SourceWarningEvent) => void;\n /**\n * Event emitted when a new audio track is added to the player.\n */\n onAudioAdded?: (event: AudioAddedEvent) => void;\n /**\n * Event emitted when the player's selected audio track has changed.\n */\n onAudioChanged?: (event: AudioChangedEvent) => void;\n /**\n * Event emitted when an audio track is removed from the player.\n */\n onAudioRemoved?: (event: AudioRemovedEvent) => void;\n /**\n * Event emitted when a new subtitle track is added to the player.\n */\n onSubtitleAdded?: (event: SubtitleAddedEvent) => void;\n /**\n * Event emitted when the player's selected subtitle track has changed.\n */\n onSubtitleChanged?: (event: SubtitleChangedEvent) => void;\n /**\n * Event emitted when a subtitle track is removed from the player.\n */\n onSubtitleRemoved?: (event: SubtitleRemovedEvent) => void;\n /**\n * Event emitted when the current playback time has changed.\n */\n onTimeChanged?: (event: TimeChangedEvent) => void;\n /**\n * Emitted when the player is unmuted.\n */\n onUnmuted?: (event: UnmutedEvent) => void;\n /**\n * Emitted when current video download quality has changed.\n */\n onVideoDownloadQualityChanged?: (\n event: VideoDownloadQualityChangedEvent\n ) => void;\n /**\n * Emitted when the current video playback quality has changed.\n */\n onVideoPlaybackQualityChanged?: (\n event: VideoPlaybackQualityChangedEvent\n ) => void;\n};\n"]} \ No newline at end of file diff --git a/build/components/PlayerView/index.d.ts b/build/components/PlayerView/index.d.ts new file mode 100644 index 00000000..cd48fabc --- /dev/null +++ b/build/components/PlayerView/index.d.ts @@ -0,0 +1,10 @@ +import React from 'react'; +import { PlayerViewProps } from './properties'; +/** + * Component that provides the Bitmovin Player UI and default UI handling to an attached `Player` instance. + * This component needs a `Player` instance to work properly so make sure one is passed to it as a prop. + * + * @param options configuration options + */ +export declare function PlayerView({ viewRef, style, player, config, fullscreenHandler, customMessageHandler, isFullscreenRequested, scalingMode, isPictureInPictureRequested, ...props }: PlayerViewProps): React.JSX.Element | null; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/build/components/PlayerView/index.d.ts.map b/build/components/PlayerView/index.d.ts.map new file mode 100644 index 00000000..8469beba --- /dev/null +++ b/build/components/PlayerView/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/PlayerView/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAO3D,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAW/C;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,KAAK,EACL,MAAM,EACN,MAAM,EACN,iBAAiB,EACjB,oBAAoB,EACpB,qBAA6B,EAC7B,WAAW,EACX,2BAAmC,EACnC,GAAG,KAAK,EACT,EAAE,eAAe,4BAuJjB"} \ No newline at end of file diff --git a/build/components/PlayerView/index.js b/build/components/PlayerView/index.js new file mode 100644 index 00000000..887d4f3b --- /dev/null +++ b/build/components/PlayerView/index.js @@ -0,0 +1,75 @@ +import React, { useRef, useEffect, useState } from 'react'; +import { StyleSheet } from 'react-native'; +import { useKeepAwake } from 'expo-keep-awake'; +import { NativePlayerView } from './native'; +import { useProxy } from '../../hooks/useProxy'; +import { FullscreenHandlerBridge } from '../../ui/fullscreenhandlerbridge'; +import { CustomMessageHandlerBridge } from '../../ui/custommessagehandlerbridge'; +/** + * Base style that initializes the native view frame when no width/height prop has been set. + */ +const styles = StyleSheet.create({ + baseStyle: { + alignSelf: 'stretch', + }, +}); +/** + * Component that provides the Bitmovin Player UI and default UI handling to an attached `Player` instance. + * This component needs a `Player` instance to work properly so make sure one is passed to it as a prop. + * + * @param options configuration options + */ +export function PlayerView({ viewRef, style, player, config, fullscreenHandler, customMessageHandler, isFullscreenRequested = false, scalingMode, isPictureInPictureRequested = false, ...props }) { + // Keep the device awake while the PlayerView is mounted + useKeepAwake(); + const nativeView = useRef(viewRef?.current || null); + // Native events proxy helper. + const proxy = useProxy(nativeView); + // Style resulting from merging `baseStyle` and `props.style`. + const nativeViewStyle = StyleSheet.flatten([styles.baseStyle, style]); + const fullscreenBridge = useRef(undefined); + if (fullscreenHandler && !fullscreenBridge.current) { + fullscreenBridge.current = new FullscreenHandlerBridge(); + } + if (fullscreenBridge.current) { + fullscreenBridge.current.setFullscreenHandler(fullscreenHandler); + } + const customMessageHandlerBridge = useRef(undefined); + if (customMessageHandler && !customMessageHandlerBridge.current) { + customMessageHandlerBridge.current = new CustomMessageHandlerBridge(); + } + if (customMessageHandlerBridge.current && customMessageHandler) { + customMessageHandlerBridge.current.setCustomMessageHandler(customMessageHandler); + } + const nativePlayerViewConfig = { + playerId: player.nativeId, + customMessageHandlerBridgeId: customMessageHandlerBridge.current?.nativeId, + enableBackgroundPlayback: player.config?.playbackConfig?.isBackgroundPlaybackEnabled, + isPictureInPictureEnabledOnPlayer: player.config?.playbackConfig?.isPictureInPictureEnabled, + userInterfaceTypeName: player.config?.styleConfig?.userInterfaceType, + playerViewConfig: config, + }; + const [isPlayerInitialized, setIsPlayerInitialized] = useState(false); + useEffect(() => { + player.initialize().then(() => { + setIsPlayerInitialized(true); + // call attach player on native view if switched to AsyncFunction for RNPlayerViewExpo + }); + return () => { + fullscreenBridge.current?.destroy(); + fullscreenBridge.current = undefined; + customMessageHandlerBridge.current?.destroy(); + customMessageHandlerBridge.current = undefined; + }; + }, [player, fullscreenBridge, customMessageHandlerBridge]); + useEffect(() => { + if (isPlayerInitialized && viewRef) { + viewRef.current = nativeView.current; + } + }, [isPlayerInitialized, viewRef, nativeView]); + if (!isPlayerInitialized) { + return null; + } + return (); +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/build/components/PlayerView/index.js.map b/build/components/PlayerView/index.js.map new file mode 100644 index 00000000..1ba95a87 --- /dev/null +++ b/build/components/PlayerView/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/components/PlayerView/index.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useRef, useEffect, useState } from 'react';\nimport { StyleSheet } from 'react-native';\nimport { useKeepAwake } from 'expo-keep-awake';\nimport { NativePlayerView, NativePlayerViewConfig } from './native';\nimport { useProxy } from '../../hooks/useProxy';\nimport { FullscreenHandlerBridge } from '../../ui/fullscreenhandlerbridge';\nimport { CustomMessageHandlerBridge } from '../../ui/custommessagehandlerbridge';\nimport { PlayerViewProps } from './properties';\n\n/**\n * Base style that initializes the native view frame when no width/height prop has been set.\n */\nconst styles = StyleSheet.create({\n baseStyle: {\n alignSelf: 'stretch',\n },\n});\n\n/**\n * Component that provides the Bitmovin Player UI and default UI handling to an attached `Player` instance.\n * This component needs a `Player` instance to work properly so make sure one is passed to it as a prop.\n *\n * @param options configuration options\n */\nexport function PlayerView({\n viewRef,\n style,\n player,\n config,\n fullscreenHandler,\n customMessageHandler,\n isFullscreenRequested = false,\n scalingMode,\n isPictureInPictureRequested = false,\n ...props\n}: PlayerViewProps) {\n // Keep the device awake while the PlayerView is mounted\n useKeepAwake();\n\n const nativeView = useRef(viewRef?.current || null);\n\n // Native events proxy helper.\n const proxy = useProxy(nativeView);\n // Style resulting from merging `baseStyle` and `props.style`.\n const nativeViewStyle = StyleSheet.flatten([styles.baseStyle, style]);\n\n const fullscreenBridge: React.RefObject =\n useRef(undefined);\n if (fullscreenHandler && !fullscreenBridge.current) {\n fullscreenBridge.current = new FullscreenHandlerBridge();\n }\n if (fullscreenBridge.current) {\n fullscreenBridge.current.setFullscreenHandler(fullscreenHandler);\n }\n\n const customMessageHandlerBridge: React.RefObject<\n CustomMessageHandlerBridge | undefined\n > = useRef(undefined);\n if (customMessageHandler && !customMessageHandlerBridge.current) {\n customMessageHandlerBridge.current = new CustomMessageHandlerBridge();\n }\n if (customMessageHandlerBridge.current && customMessageHandler) {\n customMessageHandlerBridge.current.setCustomMessageHandler(\n customMessageHandler\n );\n }\n\n const nativePlayerViewConfig: NativePlayerViewConfig = {\n playerId: player.nativeId,\n customMessageHandlerBridgeId: customMessageHandlerBridge.current?.nativeId,\n enableBackgroundPlayback:\n player.config?.playbackConfig?.isBackgroundPlaybackEnabled,\n isPictureInPictureEnabledOnPlayer:\n player.config?.playbackConfig?.isPictureInPictureEnabled,\n userInterfaceTypeName: player.config?.styleConfig?.userInterfaceType,\n playerViewConfig: config,\n };\n\n const [isPlayerInitialized, setIsPlayerInitialized] = useState(false);\n\n useEffect(() => {\n player.initialize().then(() => {\n setIsPlayerInitialized(true);\n // call attach player on native view if switched to AsyncFunction for RNPlayerViewExpo\n });\n\n return () => {\n fullscreenBridge.current?.destroy();\n fullscreenBridge.current = undefined;\n customMessageHandlerBridge.current?.destroy();\n customMessageHandlerBridge.current = undefined;\n };\n }, [player, fullscreenBridge, customMessageHandlerBridge]);\n\n useEffect(() => {\n if (isPlayerInitialized && viewRef) {\n viewRef.current = nativeView.current;\n }\n }, [isPlayerInitialized, viewRef, nativeView]);\n\n if (!isPlayerInitialized) {\n return null;\n }\n\n return (\n \n );\n}\n"]} \ No newline at end of file diff --git a/build/components/PlayerView/native.d.ts b/build/components/PlayerView/native.d.ts new file mode 100644 index 00000000..5f906521 --- /dev/null +++ b/build/components/PlayerView/native.d.ts @@ -0,0 +1,30 @@ +import { NativePlayerViewEvents } from './nativeEvents'; +import { ViewStyle } from 'react-native'; +import { ScalingMode } from '../../styleConfig'; +import { PlayerViewConfig } from './playerViewConfig'; +export interface NativePlayerViewConfig { + playerViewConfig?: PlayerViewConfig; + playerId: string; + customMessageHandlerBridgeId?: string; + enableBackgroundPlayback?: boolean; + isPictureInPictureEnabledOnPlayer?: boolean; + userInterfaceTypeName?: string; +} +/** + * Props type for `NativePlayerView` native component. + * Mostly maps the event props defined in native code. + */ +export interface NativePlayerViewProps extends NativePlayerViewEvents { + ref?: React.RefObject; + isFullscreenRequested?: boolean; + scalingMode?: ScalingMode; + isPictureInPictureRequested?: boolean; + style?: ViewStyle; + config: NativePlayerViewConfig; + fullscreenBridgeId?: string; +} +/** + * Native host component bridging Bitmovin's `PlayerView`. + */ +export declare const NativePlayerView: import("react").ComponentType; +//# sourceMappingURL=native.d.ts.map \ No newline at end of file diff --git a/build/components/PlayerView/native.d.ts.map b/build/components/PlayerView/native.d.ts.map new file mode 100644 index 00000000..b8e56453 --- /dev/null +++ b/build/components/PlayerView/native.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../../../src/components/PlayerView/native.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,MAAM,WAAW,sBAAsB;IACrC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,4BAA4B,CAAC,EAAE,MAAM,CAAC;IACtC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,iCAAiC,CAAC,EAAE,OAAO,CAAC;IAC5C,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAsB,SAAQ,sBAAsB;IACnE,GAAG,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,sDAE5B,CAAC"} \ No newline at end of file diff --git a/build/components/PlayerView/native.js b/build/components/PlayerView/native.js new file mode 100644 index 00000000..7856def9 --- /dev/null +++ b/build/components/PlayerView/native.js @@ -0,0 +1,6 @@ +import { requireNativeViewManager } from 'expo-modules-core'; +/** + * Native host component bridging Bitmovin's `PlayerView`. + */ +export const NativePlayerView = requireNativeViewManager('RNPlayerViewManager'); +//# sourceMappingURL=native.js.map \ No newline at end of file diff --git a/build/components/PlayerView/native.js.map b/build/components/PlayerView/native.js.map new file mode 100644 index 00000000..b4bd00bb --- /dev/null +++ b/build/components/PlayerView/native.js.map @@ -0,0 +1 @@ +{"version":3,"file":"native.js","sourceRoot":"","sources":["../../../src/components/PlayerView/native.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AA6B7D;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,wBAAwB,CACtD,qBAAqB,CACtB,CAAC","sourcesContent":["import { requireNativeViewManager } from 'expo-modules-core';\nimport { NativePlayerViewEvents } from './nativeEvents';\nimport { ViewStyle } from 'react-native';\nimport { ScalingMode } from '../../styleConfig';\nimport { PlayerViewConfig } from './playerViewConfig';\n\nexport interface NativePlayerViewConfig {\n playerViewConfig?: PlayerViewConfig;\n playerId: string;\n customMessageHandlerBridgeId?: string;\n enableBackgroundPlayback?: boolean;\n isPictureInPictureEnabledOnPlayer?: boolean;\n userInterfaceTypeName?: string;\n}\n\n/**\n * Props type for `NativePlayerView` native component.\n * Mostly maps the event props defined in native code.\n */\nexport interface NativePlayerViewProps extends NativePlayerViewEvents {\n ref?: React.RefObject;\n isFullscreenRequested?: boolean;\n scalingMode?: ScalingMode;\n isPictureInPictureRequested?: boolean;\n style?: ViewStyle;\n config: NativePlayerViewConfig;\n fullscreenBridgeId?: string;\n}\n\n/**\n * Native host component bridging Bitmovin's `PlayerView`.\n */\nexport const NativePlayerView = requireNativeViewManager(\n 'RNPlayerViewManager'\n);\n"]} \ No newline at end of file diff --git a/build/components/PlayerView/nativeEvents.d.ts b/build/components/PlayerView/nativeEvents.d.ts new file mode 100644 index 00000000..8f8eab22 --- /dev/null +++ b/build/components/PlayerView/nativeEvents.d.ts @@ -0,0 +1,437 @@ +import { AdBreakFinishedEvent, AdBreakStartedEvent, AdClickedEvent, AdErrorEvent, AdFinishedEvent, AdManifestLoadedEvent, AdManifestLoadEvent, AdQuartileEvent, AdScheduledEvent, AdSkippedEvent, AdStartedEvent, CastAvailableEvent, CastPausedEvent, CastPlaybackFinishedEvent, CastPlayingEvent, CastStartedEvent, CastStartEvent, CastStoppedEvent, CastTimeUpdatedEvent, CastWaitingForDeviceEvent, DestroyEvent, Event, FullscreenEnabledEvent, FullscreenDisabledEvent, FullscreenEnterEvent, FullscreenExitEvent, MutedEvent, PausedEvent, PictureInPictureAvailabilityChangedEvent, PictureInPictureEnterEvent, PictureInPictureEnteredEvent, PictureInPictureExitEvent, PictureInPictureExitedEvent, PlaybackFinishedEvent, PlayerActiveEvent, PlayerErrorEvent, PlayerWarningEvent, PlayEvent, PlayingEvent, ReadyEvent, SeekedEvent, SeekEvent, TimeShiftEvent, TimeShiftedEvent, StallStartedEvent, StallEndedEvent, SourceErrorEvent, SourceLoadedEvent, SourceLoadEvent, SourceUnloadedEvent, SourceWarningEvent, AudioAddedEvent, AudioChangedEvent, AudioRemovedEvent, SubtitleAddedEvent, SubtitleChangedEvent, SubtitleRemovedEvent, TimeChangedEvent, UnmutedEvent, VideoPlaybackQualityChangedEvent, DownloadFinishedEvent, VideoDownloadQualityChangedEvent, PlaybackSpeedChangedEvent, CueEnterEvent, CueExitEvent } from '../../events'; +/** + * Event props for `NativePlayerView`. + */ +export type NativePlayerViewEvents = { + /** + * Event emitted when an ad break has finished. + */ + onBmpAdBreakFinished?: (event: { + nativeEvent: AdBreakFinishedEvent; + }) => void; + /** + * Event emitted when an ad break has started. + */ + onBmpAdBreakStarted?: (event: { + nativeEvent: AdBreakStartedEvent; + }) => void; + /** + * Event emitted when an ad has been clicked. + */ + onBmpAdClicked?: (event: { + nativeEvent: AdClickedEvent; + }) => void; + /** + * Event emitted when an ad error has occurred. + */ + onBmpAdError?: (event: { + nativeEvent: AdErrorEvent; + }) => void; + /** + * Event emitted when an ad has finished. + */ + onBmpAdFinished?: (event: { + nativeEvent: AdFinishedEvent; + }) => void; + /** + * Event emitted when an ad manifest starts loading. + */ + onBmpAdManifestLoad?: (event: { + nativeEvent: AdManifestLoadEvent; + }) => void; + /** + * Event emitted when an ad manifest has been loaded. + */ + onBmpAdManifestLoaded?: (event: { + nativeEvent: AdManifestLoadedEvent; + }) => void; + /** + * Event emitted when an ad quartile has been reached. + */ + onBmpAdQuartile?: (event: { + nativeEvent: AdQuartileEvent; + }) => void; + /** + * Event emitted when an ad has been scheduled. + */ + onBmpAdScheduled?: (event: { + nativeEvent: AdScheduledEvent; + }) => void; + /** + * Event emitted when an ad has been skipped. + */ + onBmpAdSkipped?: (event: { + nativeEvent: AdSkippedEvent; + }) => void; + /** + * Event emitted when an ad has started. + */ + onBmpAdStarted?: (event: { + nativeEvent: AdStartedEvent; + }) => void; + /** + * Event emitted when casting to a cast-compatible device is available. + * + * @remarks Platform: iOS, Android + */ + onBmpCastAvailable?: (event: { + nativeEvent: CastAvailableEvent; + }) => void; + /** + * Event emitted when the playback on a cast-compatible device was paused. + * + * @remarks Platform: iOS, Android + */ + onBmpCastPaused?: (event: { + nativeEvent: CastPausedEvent; + }) => void; + /** + * Event emitted when the playback on a cast-compatible device has finished. + * + * @remarks Platform: iOS, Android + */ + onBmpCastPlaybackFinished?: (event: { + nativeEvent: CastPlaybackFinishedEvent; + }) => void; + /** + * Event emitted when playback on a cast-compatible device has started. + * + * @remarks Platform: iOS, Android + */ + onBmpCastPlaying?: (event: { + nativeEvent: CastPlayingEvent; + }) => void; + /** + * Event emitted when the cast app is launched successfully. + * + * @remarks Platform: iOS, Android + */ + onBmpCastStarted?: (event: { + nativeEvent: CastStartedEvent; + }) => void; + /** + * Event emitted when casting is initiated, but the user still needs to choose which device should be used. + * + * @remarks Platform: iOS, Android + */ + onBmpCastStart?: (event: { + nativeEvent: CastStartEvent; + }) => void; + /** + * Event emitted when casting to a cast-compatible device is stopped. + * + * @remarks Platform: iOS, Android + */ + onBmpCastStopped?: (event: { + nativeEvent: CastStoppedEvent; + }) => void; + /** + * Event emitted when the time update from the currently used cast-compatible device is received. + * + * @remarks Platform: iOS, Android + */ + onBmpCastTimeUpdated?: (event: { + nativeEvent: CastTimeUpdatedEvent; + }) => void; + /** + * Event emitted when a cast-compatible device has been chosen and the player is waiting for the device to get ready for + * playback. + * + * @remarks Platform: iOS, Android + */ + onBmpCastWaitingForDevice?: (event: { + nativeEvent: CastWaitingForDeviceEvent; + }) => void; + /** + * Event emitted when a subtitle entry transitions into the active status. + */ + onBmpCueEnter?: (event: { + nativeEvent: CueEnterEvent; + }) => void; + /** + * Event emitted when an active subtitle entry transitions into the inactive status. + */ + onBmpCueExit?: (event: { + nativeEvent: CueExitEvent; + }) => void; + /** + * Event emitted when the player is destroyed. + */ + onBmpDestroy?: (event: { + nativeEvent: DestroyEvent; + }) => void; + /** + * Emitted when a download was finished. + */ + onBmpDownloadFinished?: (event: { + nativeEvent: DownloadFinishedEvent; + }) => void; + /** + * All events emitted by the player. + */ + onBmpEvent?: (event: { + nativeEvent: Event; + }) => void; + /** + * Event emitted when fullscreen mode has been enabled. + * + * @remarks Platform: iOS, Android + */ + onBmpFullscreenEnabled?: (event: { + nativeEvent: FullscreenEnabledEvent; + }) => void; + /** + * Event emitted when fullscreen mode has been disabled. + * + * @remarks Platform: iOS, Android + */ + onBmpFullscreenDisabled?: (event: { + nativeEvent: FullscreenDisabledEvent; + }) => void; + /** + * Event emitted when fullscreen mode has been entered. + * + * @remarks Platform: iOS, Android + */ + onBmpFullscreenEnter?: (event: { + nativeEvent: FullscreenEnterEvent; + }) => void; + /** + * Event emitted when fullscreen mode has been exited. + * + * @remarks Platform: iOS, Android + */ + onBmpFullscreenExit?: (event: { + nativeEvent: FullscreenExitEvent; + }) => void; + /** + * Event emitted when the player has been muted. + */ + onBmpMuted?: (event: { + nativeEvent: MutedEvent; + }) => void; + /** + * Event emitted when the player has been paused. + */ + onBmpPaused?: (event: { + nativeEvent: PausedEvent; + }) => void; + /** + * Event mitted when the availability of the Picture in Picture mode changed. + */ + onBmpPictureInPictureAvailabilityChanged?: (event: { + nativeEvent: PictureInPictureAvailabilityChangedEvent; + }) => void; + /** + * Event emitted when the player enters Picture in Picture mode. + */ + onBmpPictureInPictureEnter?: (event: { + nativeEvent: PictureInPictureEnterEvent; + }) => void; + /** + * Event emitted when the player entered Picture in Picture mode. + * + * @remarks Platform: iOS + */ + onBmpPictureInPictureEntered?: (event: { + nativeEvent: PictureInPictureEnteredEvent; + }) => void; + /** + * Event emitted when the player exits Picture in Picture mode. + */ + onBmpPictureInPictureExit?: (event: { + nativeEvent: PictureInPictureExitEvent; + }) => void; + /** + * Event emitted when the player exited Picture in Picture mode. + * + * @remarks Platform: iOS + */ + onBmpPictureInPictureExited?: (event: { + nativeEvent: PictureInPictureExitedEvent; + }) => void; + /** + * Event emitted when the player received an intention to start/resume playback. + */ + onBmpPlay?: (event: { + nativeEvent: PlayEvent; + }) => void; + /** + * Event emitted when the playback of the current media has finished. + */ + onBmpPlaybackFinished?: (event: { + nativeEvent: PlaybackFinishedEvent; + }) => void; + /** + * Emitted when the player transitions from one playback speed to another. + * @remarks Platform: iOS, tvOS + */ + onBmpPlaybackSpeedChanged?: (event: { + nativeEvent: PlaybackSpeedChangedEvent; + }) => void; + /** + * Event emitted when a source is loaded into the player. + * Seeking and time shifting are allowed as soon as this event is seen. + */ + onBmpPlayerActive?: (event: { + nativeEvent: PlayerActiveEvent; + }) => void; + /** + * Event Emitted when a player error occurred. + */ + onBmpPlayerError?: (event: { + nativeEvent: PlayerErrorEvent; + }) => void; + /** + * Event emitted when a player warning occurred. + */ + onBmpPlayerWarning?: (event: { + nativeEvent: PlayerWarningEvent; + }) => void; + /** + * Emitted when playback has started. + */ + onBmpPlaying?: (event: { + nativeEvent: PlayingEvent; + }) => void; + /** + * Emitted when the player is ready for immediate playback, because initial audio/video + * has been downloaded. + */ + onBmpReady?: (event: { + nativeEvent: ReadyEvent; + }) => void; + /** + * Event emitted when the player is about to seek to a new position. + * Only applies to VoD streams. + */ + onBmpSeek?: (event: { + nativeEvent: SeekEvent; + }) => void; + /** + * Event emitted when seeking has finished and data to continue playback is available. + * Only applies to VoD streams. + */ + onBmpSeeked?: (event: { + nativeEvent: SeekedEvent; + }) => void; + /** + * Event mitted when the player starts time shifting. + * Only applies to live streams. + */ + onBmpTimeShift?: (event: { + nativeEvent: TimeShiftEvent; + }) => void; + /** + * Event emitted when time shifting has finished and data is available to continue playback. + * Only applies to live streams. + */ + onBmpTimeShifted?: (event: { + nativeEvent: TimeShiftedEvent; + }) => void; + /** + * Event emitted when the player begins to stall and to buffer due to an empty buffer. + */ + onBmpStallStarted?: (event: { + nativeEvent: StallStartedEvent; + }) => void; + /** + * Event emitted when the player ends stalling, due to enough data in the buffer. + */ + onBmpStallEnded?: (event: { + nativeEvent: StallEndedEvent; + }) => void; + /** + * Event emitted when a source error occurred. + */ + onBmpSourceError?: (event: { + nativeEvent: SourceErrorEvent; + }) => void; + /** + * Event emitted when a new source loading has started. + */ + onBmpSourceLoad?: (event: { + nativeEvent: SourceLoadEvent; + }) => void; + /** + * Event emitted when a new source is loaded. + * This does not mean that the source is immediately ready for playback. + * `ReadyEvent` indicates the player is ready for immediate playback. + */ + onBmpSourceLoaded?: (event: { + nativeEvent: SourceLoadedEvent; + }) => void; + /** + * Event emitted when the current source has been unloaded. + */ + onBmpSourceUnloaded?: (event: { + nativeEvent: SourceUnloadedEvent; + }) => void; + /** + * Event emitted when a source warning occurred. + */ + onBmpSourceWarning?: (event: { + nativeEvent: SourceWarningEvent; + }) => void; + /** + * Event emitted when a new audio track is added to the player. + */ + onBmpAudioAdded?: (event: { + nativeEvent: AudioAddedEvent; + }) => void; + /** + * Event emitted when the player's selected audio track has changed. + */ + onBmpAudioChanged?: (event: { + nativeEvent: AudioChangedEvent; + }) => void; + /** + * Event emitted when an audio track is removed from the player. + */ + onBmpAudioRemoved?: (event: { + nativeEvent: AudioRemovedEvent; + }) => void; + /** + * Event emitted when a new subtitle track is added to the player. + */ + onBmpSubtitleAdded?: (event: { + nativeEvent: SubtitleAddedEvent; + }) => void; + /** + * Event emitted when the player's selected subtitle track has changed. + */ + onBmpSubtitleChanged?: (event: { + nativeEvent: SubtitleChangedEvent; + }) => void; + /** + * Event emitted when a subtitle track is removed from the player. + */ + onBmpSubtitleRemoved?: (event: { + nativeEvent: SubtitleRemovedEvent; + }) => void; + /** + * Event emitted when the current playback time has changed. + */ + onBmpTimeChanged?: (event: { + nativeEvent: TimeChangedEvent; + }) => void; + /** + * Emitted when the player is unmuted. + */ + onBmpUnmuted?: (event: { + nativeEvent: UnmutedEvent; + }) => void; + /** + * Emitted when current video download quality has changed. + */ + onBmpVideoDownloadQualityChanged?: (event: { + nativeEvent: VideoDownloadQualityChangedEvent; + }) => void; + /** + * Emitted when the current video playback quality has changed. + */ + onBmpVideoPlaybackQualityChanged?: (event: { + nativeEvent: VideoPlaybackQualityChangedEvent; + }) => void; +}; +//# sourceMappingURL=nativeEvents.d.ts.map \ No newline at end of file diff --git a/build/components/PlayerView/nativeEvents.d.ts.map b/build/components/PlayerView/nativeEvents.d.ts.map new file mode 100644 index 00000000..0f2e1cb9 --- /dev/null +++ b/build/components/PlayerView/nativeEvents.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"nativeEvents.d.ts","sourceRoot":"","sources":["../../../src/components/PlayerView/nativeEvents.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/build/components/PlayerView/nativeEvents.js b/build/components/PlayerView/nativeEvents.js new file mode 100644 index 00000000..aadac2d9 --- /dev/null +++ b/build/components/PlayerView/nativeEvents.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=nativeEvents.js.map \ No newline at end of file diff --git a/build/components/PlayerView/nativeEvents.js.map b/build/components/PlayerView/nativeEvents.js.map new file mode 100644 index 00000000..923811c2 --- /dev/null +++ b/build/components/PlayerView/nativeEvents.js.map @@ -0,0 +1 @@ +{"version":3,"file":"nativeEvents.js","sourceRoot":"","sources":["../../../src/components/PlayerView/nativeEvents.ts"],"names":[],"mappings":"","sourcesContent":["import {\n AdBreakFinishedEvent,\n AdBreakStartedEvent,\n AdClickedEvent,\n AdErrorEvent,\n AdFinishedEvent,\n AdManifestLoadedEvent,\n AdManifestLoadEvent,\n AdQuartileEvent,\n AdScheduledEvent,\n AdSkippedEvent,\n AdStartedEvent,\n CastAvailableEvent,\n CastPausedEvent,\n CastPlaybackFinishedEvent,\n CastPlayingEvent,\n CastStartedEvent,\n CastStartEvent,\n CastStoppedEvent,\n CastTimeUpdatedEvent,\n CastWaitingForDeviceEvent,\n DestroyEvent,\n Event,\n FullscreenEnabledEvent,\n FullscreenDisabledEvent,\n FullscreenEnterEvent,\n FullscreenExitEvent,\n MutedEvent,\n PausedEvent,\n PictureInPictureAvailabilityChangedEvent,\n PictureInPictureEnterEvent,\n PictureInPictureEnteredEvent,\n PictureInPictureExitEvent,\n PictureInPictureExitedEvent,\n PlaybackFinishedEvent,\n PlayerActiveEvent,\n PlayerErrorEvent,\n PlayerWarningEvent,\n PlayEvent,\n PlayingEvent,\n ReadyEvent,\n SeekedEvent,\n SeekEvent,\n TimeShiftEvent,\n TimeShiftedEvent,\n StallStartedEvent,\n StallEndedEvent,\n SourceErrorEvent,\n SourceLoadedEvent,\n SourceLoadEvent,\n SourceUnloadedEvent,\n SourceWarningEvent,\n AudioAddedEvent,\n AudioChangedEvent,\n AudioRemovedEvent,\n SubtitleAddedEvent,\n SubtitleChangedEvent,\n SubtitleRemovedEvent,\n TimeChangedEvent,\n UnmutedEvent,\n VideoPlaybackQualityChangedEvent,\n DownloadFinishedEvent,\n VideoDownloadQualityChangedEvent,\n PlaybackSpeedChangedEvent,\n CueEnterEvent,\n CueExitEvent,\n} from '../../events';\n\n/**\n * Event props for `NativePlayerView`.\n */\nexport type NativePlayerViewEvents = {\n /**\n * Event emitted when an ad break has finished.\n */\n onBmpAdBreakFinished?: (event: { nativeEvent: AdBreakFinishedEvent }) => void;\n /**\n * Event emitted when an ad break has started.\n */\n onBmpAdBreakStarted?: (event: { nativeEvent: AdBreakStartedEvent }) => void;\n /**\n * Event emitted when an ad has been clicked.\n */\n onBmpAdClicked?: (event: { nativeEvent: AdClickedEvent }) => void;\n /**\n * Event emitted when an ad error has occurred.\n */\n onBmpAdError?: (event: { nativeEvent: AdErrorEvent }) => void;\n /**\n * Event emitted when an ad has finished.\n */\n onBmpAdFinished?: (event: { nativeEvent: AdFinishedEvent }) => void;\n /**\n * Event emitted when an ad manifest starts loading.\n */\n onBmpAdManifestLoad?: (event: { nativeEvent: AdManifestLoadEvent }) => void;\n /**\n * Event emitted when an ad manifest has been loaded.\n */\n onBmpAdManifestLoaded?: (event: {\n nativeEvent: AdManifestLoadedEvent;\n }) => void;\n /**\n * Event emitted when an ad quartile has been reached.\n */\n onBmpAdQuartile?: (event: { nativeEvent: AdQuartileEvent }) => void;\n /**\n * Event emitted when an ad has been scheduled.\n */\n onBmpAdScheduled?: (event: { nativeEvent: AdScheduledEvent }) => void;\n /**\n * Event emitted when an ad has been skipped.\n */\n onBmpAdSkipped?: (event: { nativeEvent: AdSkippedEvent }) => void;\n /**\n * Event emitted when an ad has started.\n */\n onBmpAdStarted?: (event: { nativeEvent: AdStartedEvent }) => void;\n /**\n * Event emitted when casting to a cast-compatible device is available.\n *\n * @remarks Platform: iOS, Android\n */\n onBmpCastAvailable?: (event: { nativeEvent: CastAvailableEvent }) => void;\n /**\n * Event emitted when the playback on a cast-compatible device was paused.\n *\n * @remarks Platform: iOS, Android\n */\n onBmpCastPaused?: (event: { nativeEvent: CastPausedEvent }) => void;\n /**\n * Event emitted when the playback on a cast-compatible device has finished.\n *\n * @remarks Platform: iOS, Android\n */\n onBmpCastPlaybackFinished?: (event: {\n nativeEvent: CastPlaybackFinishedEvent;\n }) => void;\n /**\n * Event emitted when playback on a cast-compatible device has started.\n *\n * @remarks Platform: iOS, Android\n */\n onBmpCastPlaying?: (event: { nativeEvent: CastPlayingEvent }) => void;\n /**\n * Event emitted when the cast app is launched successfully.\n *\n * @remarks Platform: iOS, Android\n */\n onBmpCastStarted?: (event: { nativeEvent: CastStartedEvent }) => void;\n /**\n * Event emitted when casting is initiated, but the user still needs to choose which device should be used.\n *\n * @remarks Platform: iOS, Android\n */\n onBmpCastStart?: (event: { nativeEvent: CastStartEvent }) => void;\n /**\n * Event emitted when casting to a cast-compatible device is stopped.\n *\n * @remarks Platform: iOS, Android\n */\n onBmpCastStopped?: (event: { nativeEvent: CastStoppedEvent }) => void;\n /**\n * Event emitted when the time update from the currently used cast-compatible device is received.\n *\n * @remarks Platform: iOS, Android\n */\n onBmpCastTimeUpdated?: (event: { nativeEvent: CastTimeUpdatedEvent }) => void;\n /**\n * Event emitted when a cast-compatible device has been chosen and the player is waiting for the device to get ready for\n * playback.\n *\n * @remarks Platform: iOS, Android\n */\n onBmpCastWaitingForDevice?: (event: {\n nativeEvent: CastWaitingForDeviceEvent;\n }) => void;\n /**\n * Event emitted when a subtitle entry transitions into the active status.\n */\n onBmpCueEnter?: (event: { nativeEvent: CueEnterEvent }) => void;\n /**\n * Event emitted when an active subtitle entry transitions into the inactive status.\n */\n onBmpCueExit?: (event: { nativeEvent: CueExitEvent }) => void;\n /**\n * Event emitted when the player is destroyed.\n */\n onBmpDestroy?: (event: { nativeEvent: DestroyEvent }) => void;\n /**\n * Emitted when a download was finished.\n */\n onBmpDownloadFinished?: (event: {\n nativeEvent: DownloadFinishedEvent;\n }) => void;\n /**\n * All events emitted by the player.\n */\n onBmpEvent?: (event: { nativeEvent: Event }) => void;\n /**\n * Event emitted when fullscreen mode has been enabled.\n *\n * @remarks Platform: iOS, Android\n */\n onBmpFullscreenEnabled?: (event: {\n nativeEvent: FullscreenEnabledEvent;\n }) => void;\n /**\n * Event emitted when fullscreen mode has been disabled.\n *\n * @remarks Platform: iOS, Android\n */\n onBmpFullscreenDisabled?: (event: {\n nativeEvent: FullscreenDisabledEvent;\n }) => void;\n /**\n * Event emitted when fullscreen mode has been entered.\n *\n * @remarks Platform: iOS, Android\n */\n onBmpFullscreenEnter?: (event: { nativeEvent: FullscreenEnterEvent }) => void;\n /**\n * Event emitted when fullscreen mode has been exited.\n *\n * @remarks Platform: iOS, Android\n */\n onBmpFullscreenExit?: (event: { nativeEvent: FullscreenExitEvent }) => void;\n /**\n * Event emitted when the player has been muted.\n */\n onBmpMuted?: (event: { nativeEvent: MutedEvent }) => void;\n /**\n * Event emitted when the player has been paused.\n */\n onBmpPaused?: (event: { nativeEvent: PausedEvent }) => void;\n /**\n * Event mitted when the availability of the Picture in Picture mode changed.\n */\n onBmpPictureInPictureAvailabilityChanged?: (event: {\n nativeEvent: PictureInPictureAvailabilityChangedEvent;\n }) => void;\n /**\n * Event emitted when the player enters Picture in Picture mode.\n */\n onBmpPictureInPictureEnter?: (event: {\n nativeEvent: PictureInPictureEnterEvent;\n }) => void;\n /**\n * Event emitted when the player entered Picture in Picture mode.\n *\n * @remarks Platform: iOS\n */\n onBmpPictureInPictureEntered?: (event: {\n nativeEvent: PictureInPictureEnteredEvent;\n }) => void;\n /**\n * Event emitted when the player exits Picture in Picture mode.\n */\n onBmpPictureInPictureExit?: (event: {\n nativeEvent: PictureInPictureExitEvent;\n }) => void;\n /**\n * Event emitted when the player exited Picture in Picture mode.\n *\n * @remarks Platform: iOS\n */\n onBmpPictureInPictureExited?: (event: {\n nativeEvent: PictureInPictureExitedEvent;\n }) => void;\n /**\n * Event emitted when the player received an intention to start/resume playback.\n */\n onBmpPlay?: (event: { nativeEvent: PlayEvent }) => void;\n /**\n * Event emitted when the playback of the current media has finished.\n */\n onBmpPlaybackFinished?: (event: {\n nativeEvent: PlaybackFinishedEvent;\n }) => void;\n /**\n * Emitted when the player transitions from one playback speed to another.\n * @remarks Platform: iOS, tvOS\n */\n onBmpPlaybackSpeedChanged?: (event: {\n nativeEvent: PlaybackSpeedChangedEvent;\n }) => void;\n /**\n * Event emitted when a source is loaded into the player.\n * Seeking and time shifting are allowed as soon as this event is seen.\n */\n onBmpPlayerActive?: (event: { nativeEvent: PlayerActiveEvent }) => void;\n /**\n * Event Emitted when a player error occurred.\n */\n onBmpPlayerError?: (event: { nativeEvent: PlayerErrorEvent }) => void;\n /**\n * Event emitted when a player warning occurred.\n */\n onBmpPlayerWarning?: (event: { nativeEvent: PlayerWarningEvent }) => void;\n /**\n * Emitted when playback has started.\n */\n onBmpPlaying?: (event: { nativeEvent: PlayingEvent }) => void;\n /**\n * Emitted when the player is ready for immediate playback, because initial audio/video\n * has been downloaded.\n */\n onBmpReady?: (event: { nativeEvent: ReadyEvent }) => void;\n /**\n * Event emitted when the player is about to seek to a new position.\n * Only applies to VoD streams.\n */\n onBmpSeek?: (event: { nativeEvent: SeekEvent }) => void;\n /**\n * Event emitted when seeking has finished and data to continue playback is available.\n * Only applies to VoD streams.\n */\n onBmpSeeked?: (event: { nativeEvent: SeekedEvent }) => void;\n /**\n * Event mitted when the player starts time shifting.\n * Only applies to live streams.\n */\n onBmpTimeShift?: (event: { nativeEvent: TimeShiftEvent }) => void;\n /**\n * Event emitted when time shifting has finished and data is available to continue playback.\n * Only applies to live streams.\n */\n onBmpTimeShifted?: (event: { nativeEvent: TimeShiftedEvent }) => void;\n /**\n * Event emitted when the player begins to stall and to buffer due to an empty buffer.\n */\n onBmpStallStarted?: (event: { nativeEvent: StallStartedEvent }) => void;\n /**\n * Event emitted when the player ends stalling, due to enough data in the buffer.\n */\n onBmpStallEnded?: (event: { nativeEvent: StallEndedEvent }) => void;\n /**\n * Event emitted when a source error occurred.\n */\n onBmpSourceError?: (event: { nativeEvent: SourceErrorEvent }) => void;\n /**\n * Event emitted when a new source loading has started.\n */\n onBmpSourceLoad?: (event: { nativeEvent: SourceLoadEvent }) => void;\n /**\n * Event emitted when a new source is loaded.\n * This does not mean that the source is immediately ready for playback.\n * `ReadyEvent` indicates the player is ready for immediate playback.\n */\n onBmpSourceLoaded?: (event: { nativeEvent: SourceLoadedEvent }) => void;\n /**\n * Event emitted when the current source has been unloaded.\n */\n onBmpSourceUnloaded?: (event: { nativeEvent: SourceUnloadedEvent }) => void;\n /**\n * Event emitted when a source warning occurred.\n */\n onBmpSourceWarning?: (event: { nativeEvent: SourceWarningEvent }) => void;\n /**\n * Event emitted when a new audio track is added to the player.\n */\n onBmpAudioAdded?: (event: { nativeEvent: AudioAddedEvent }) => void;\n /**\n * Event emitted when the player's selected audio track has changed.\n */\n onBmpAudioChanged?: (event: { nativeEvent: AudioChangedEvent }) => void;\n /**\n * Event emitted when an audio track is removed from the player.\n */\n onBmpAudioRemoved?: (event: { nativeEvent: AudioRemovedEvent }) => void;\n /**\n * Event emitted when a new subtitle track is added to the player.\n */\n onBmpSubtitleAdded?: (event: { nativeEvent: SubtitleAddedEvent }) => void;\n /**\n * Event emitted when the player's selected subtitle track has changed.\n */\n onBmpSubtitleChanged?: (event: { nativeEvent: SubtitleChangedEvent }) => void;\n /**\n * Event emitted when a subtitle track is removed from the player.\n */\n onBmpSubtitleRemoved?: (event: { nativeEvent: SubtitleRemovedEvent }) => void;\n /**\n * Event emitted when the current playback time has changed.\n */\n onBmpTimeChanged?: (event: { nativeEvent: TimeChangedEvent }) => void;\n /**\n * Emitted when the player is unmuted.\n */\n onBmpUnmuted?: (event: { nativeEvent: UnmutedEvent }) => void;\n /**\n * Emitted when current video download quality has changed.\n */\n onBmpVideoDownloadQualityChanged?: (event: {\n nativeEvent: VideoDownloadQualityChangedEvent;\n }) => void;\n /**\n * Emitted when the current video playback quality has changed.\n */\n onBmpVideoPlaybackQualityChanged?: (event: {\n nativeEvent: VideoPlaybackQualityChangedEvent;\n }) => void;\n};\n"]} \ No newline at end of file diff --git a/build/components/PlayerView/pictureInPictureConfig.d.ts b/build/components/PlayerView/pictureInPictureConfig.d.ts new file mode 100644 index 00000000..ef173cd6 --- /dev/null +++ b/build/components/PlayerView/pictureInPictureConfig.d.ts @@ -0,0 +1,22 @@ +/** + * Provides options to configure Picture in Picture playback. + */ +export interface PictureInPictureConfig { + /** + * Whether Picture in Picture feature is enabled or not. + * + * Default is `false`. + */ + isEnabled?: boolean; + /** + * Defines whether Picture in Picture should start automatically when the app transitions to background. + * + * Does not have any affect when Picture in Picture is disabled. + * + * Default is `false`. + * + * @remarks Platform: iOS 14.2 and above + */ + shouldEnterOnBackground?: boolean; +} +//# sourceMappingURL=pictureInPictureConfig.d.ts.map \ No newline at end of file diff --git a/build/components/PlayerView/pictureInPictureConfig.d.ts.map b/build/components/PlayerView/pictureInPictureConfig.d.ts.map new file mode 100644 index 00000000..6ef21a50 --- /dev/null +++ b/build/components/PlayerView/pictureInPictureConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"pictureInPictureConfig.d.ts","sourceRoot":"","sources":["../../../src/components/PlayerView/pictureInPictureConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;;;;;;;OAQG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC"} \ No newline at end of file diff --git a/build/components/PlayerView/pictureInPictureConfig.js b/build/components/PlayerView/pictureInPictureConfig.js new file mode 100644 index 00000000..d5d77245 --- /dev/null +++ b/build/components/PlayerView/pictureInPictureConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=pictureInPictureConfig.js.map \ No newline at end of file diff --git a/build/components/PlayerView/pictureInPictureConfig.js.map b/build/components/PlayerView/pictureInPictureConfig.js.map new file mode 100644 index 00000000..996e2c4a --- /dev/null +++ b/build/components/PlayerView/pictureInPictureConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"pictureInPictureConfig.js","sourceRoot":"","sources":["../../../src/components/PlayerView/pictureInPictureConfig.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Provides options to configure Picture in Picture playback.\n */\nexport interface PictureInPictureConfig {\n /**\n * Whether Picture in Picture feature is enabled or not.\n *\n * Default is `false`.\n */\n isEnabled?: boolean;\n\n /**\n * Defines whether Picture in Picture should start automatically when the app transitions to background.\n *\n * Does not have any affect when Picture in Picture is disabled.\n *\n * Default is `false`.\n *\n * @remarks Platform: iOS 14.2 and above\n */\n shouldEnterOnBackground?: boolean;\n}\n"]} \ No newline at end of file diff --git a/build/components/PlayerView/playerViewConfig.d.ts b/build/components/PlayerView/playerViewConfig.d.ts new file mode 100644 index 00000000..9b9b3ec4 --- /dev/null +++ b/build/components/PlayerView/playerViewConfig.d.ts @@ -0,0 +1,116 @@ +import { PictureInPictureConfig } from './pictureInPictureConfig'; +import { SubtitleViewConfig } from './subtitleViewConfig'; +/** + * Configures the visual presentation and behaviour of the `PlayerView`. + */ +export interface PlayerViewConfig { + /** + * Configures the visual presentation and behaviour of the Bitmovin Player UI. + * A {@link WebUiConfig} can be used to configure the default Bitmovin Player Web UI. + * + * Default is {@link WebUiConfig}. + * + * Limitations: + * Configuring the `uiConfig` only has an effect if the {@link StyleConfig.userInterfaceType} is set to {@link UserInterfaceType.Bitmovin}. + */ + uiConfig?: UiConfig; + /** + * Provides options to configure Picture in Picture playback. + */ + pictureInPictureConfig?: PictureInPictureConfig; + /** + * When set to `true`, the first frame of the main content will not be rendered before playback starts. Default is `false`. + * This configuration has no effect for the {@link UserInterfaceType.Subtitle} on iOS/tvOS. + * + * To reliably hide the first frame before a pre-roll ad, please ensure that you are using the {@link AdvertisingConfig} to schedule ads and not the {@link Player.scheduleAd} API call. + */ + hideFirstFrame?: boolean; + /** + * Provides options to configure the subtitle view. + */ + subtitleViewConfig?: SubtitleViewConfig; + /** + * Specify on which surface type the video should be rendered. + * + * See {@link https://developer.android.com/guide/topics/media/ui/playerview#surfacetype|Choosing a surface type} + * for more information. + * + * Default is {@link SurfaceType.SurfaceView}. + * + * @remarks Platform: Android + */ + surfaceType?: SurfaceType; +} +/** + * Configures the visual presentation and behaviour of the Bitmovin Player UI. + */ +export type UiConfig = object; +/** + * Configures the visual presentation and behaviour of the Bitmovin Web UI. + */ +export interface WebUiConfig extends UiConfig { + /** + * Whether the Bitmovin Web UI will show playback speed selection options in the settings menu. + * Default is `true`. + */ + playbackSpeedSelectionEnabled?: boolean; + /** + * The UI variant to use for the Bitmovin Player Web UI. + * + * Default is {@link SmallScreenUi} + */ + variant?: Variant; + /** + * Whether the WebView should be focused on initialization. + * + * By default this is enabled only for the TV UI variant, as it's needed there to + * initiate spatial navigation using the remote control. + * + * @remarks Platform: Android + */ + focusUiOnInitialization?: boolean; +} +export declare abstract class Variant { + readonly uiManagerFactoryFunction: string; + /** + * Specifies the function name that will be used to initialize the `UIManager` + * for the Bitmovin Player Web UI. + * + * The function is called on the `window` object with the `Player` as the first argument and + * the `UIConfig` as the second argument. + * + * Example: + * When you added a new function or want to use a different function of our `UIFactory`, + * you can specify the full qualifier name including namespaces. + * e.g. `bitmovin.playerui.UIFactory.buildDefaultSmallScreenUI` for the SmallScreenUi. + * @see UIFactory https://github.com/bitmovin/bitmovin-player-ui/blob/develop/src/ts/uifactory.ts#L60 + * + * Notes: + * - It's not necessary to use our `UIFactory`. Any static function can be specified. + */ + constructor(uiManagerFactoryFunction: string); +} +export declare class SmallScreenUi extends Variant { + constructor(); +} +export declare class TvUi extends Variant { + constructor(); +} +export declare class CustomUi extends Variant { +} +/** + * The type of surface on which to render video. + * + * See {@link https://developer.android.com/guide/topics/media/ui/playerview#surfacetype|Choosing a surface type} + * for more information. + */ +export declare enum SurfaceType { + /** + * SurfaceView generally causes lower battery consumption, + * and has better handling for HDR and secure content. + */ + SurfaceView = "SurfaceView", + /** TextureView is sometime needed for smooth animations. */ + TextureView = "TextureView" +} +//# sourceMappingURL=playerViewConfig.d.ts.map \ No newline at end of file diff --git a/build/components/PlayerView/playerViewConfig.d.ts.map b/build/components/PlayerView/playerViewConfig.d.ts.map new file mode 100644 index 00000000..e06b8235 --- /dev/null +++ b/build/components/PlayerView/playerViewConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"playerViewConfig.d.ts","sourceRoot":"","sources":["../../../src/components/PlayerView/playerViewConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAEpB;;OAEG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAEhD;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB;;OAEG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IAExC;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,QAAQ;IAC3C;;;OAGG;IACH,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;OAOG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,8BAAsB,OAAO;aAiBC,wBAAwB,EAAE,MAAM;IAhB5D;;;;;;;;;;;;;;;OAeG;gBACyB,wBAAwB,EAAE,MAAM;CAC7D;AAED,qBAAa,aAAc,SAAQ,OAAO;;CAIzC;AAED,qBAAa,IAAK,SAAQ,OAAO;;CAIhC;AAED,qBAAa,QAAS,SAAQ,OAAO;CAAG;AAExC;;;;;GAKG;AACH,oBAAY,WAAW;IACrB;;;OAGG;IACH,WAAW,gBAAgB;IAC3B,4DAA4D;IAC5D,WAAW,gBAAgB;CAC5B"} \ No newline at end of file diff --git a/build/components/PlayerView/playerViewConfig.js b/build/components/PlayerView/playerViewConfig.js new file mode 100644 index 00000000..89d2cefc --- /dev/null +++ b/build/components/PlayerView/playerViewConfig.js @@ -0,0 +1,51 @@ +export class Variant { + uiManagerFactoryFunction; + /** + * Specifies the function name that will be used to initialize the `UIManager` + * for the Bitmovin Player Web UI. + * + * The function is called on the `window` object with the `Player` as the first argument and + * the `UIConfig` as the second argument. + * + * Example: + * When you added a new function or want to use a different function of our `UIFactory`, + * you can specify the full qualifier name including namespaces. + * e.g. `bitmovin.playerui.UIFactory.buildDefaultSmallScreenUI` for the SmallScreenUi. + * @see UIFactory https://github.com/bitmovin/bitmovin-player-ui/blob/develop/src/ts/uifactory.ts#L60 + * + * Notes: + * - It's not necessary to use our `UIFactory`. Any static function can be specified. + */ + constructor(uiManagerFactoryFunction) { + this.uiManagerFactoryFunction = uiManagerFactoryFunction; + } +} +export class SmallScreenUi extends Variant { + constructor() { + super('bitmovin.playerui.UIFactory.buildDefaultSmallScreenUI'); + } +} +export class TvUi extends Variant { + constructor() { + super('bitmovin.playerui.UIFactory.buildDefaultTvUI'); + } +} +export class CustomUi extends Variant { +} +/** + * The type of surface on which to render video. + * + * See {@link https://developer.android.com/guide/topics/media/ui/playerview#surfacetype|Choosing a surface type} + * for more information. + */ +export var SurfaceType; +(function (SurfaceType) { + /** + * SurfaceView generally causes lower battery consumption, + * and has better handling for HDR and secure content. + */ + SurfaceType["SurfaceView"] = "SurfaceView"; + /** TextureView is sometime needed for smooth animations. */ + SurfaceType["TextureView"] = "TextureView"; +})(SurfaceType || (SurfaceType = {})); +//# sourceMappingURL=playerViewConfig.js.map \ No newline at end of file diff --git a/build/components/PlayerView/playerViewConfig.js.map b/build/components/PlayerView/playerViewConfig.js.map new file mode 100644 index 00000000..da4614aa --- /dev/null +++ b/build/components/PlayerView/playerViewConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"playerViewConfig.js","sourceRoot":"","sources":["../../../src/components/PlayerView/playerViewConfig.ts"],"names":[],"mappings":"AAgFA,MAAM,OAAgB,OAAO;IAiBC;IAhB5B;;;;;;;;;;;;;;;OAeG;IACH,YAA4B,wBAAgC;QAAhC,6BAAwB,GAAxB,wBAAwB,CAAQ;IAAG,CAAC;CACjE;AAED,MAAM,OAAO,aAAc,SAAQ,OAAO;IACxC;QACE,KAAK,CAAC,uDAAuD,CAAC,CAAC;IACjE,CAAC;CACF;AAED,MAAM,OAAO,IAAK,SAAQ,OAAO;IAC/B;QACE,KAAK,CAAC,8CAA8C,CAAC,CAAC;IACxD,CAAC;CACF;AAED,MAAM,OAAO,QAAS,SAAQ,OAAO;CAAG;AAExC;;;;;GAKG;AACH,MAAM,CAAN,IAAY,WAQX;AARD,WAAY,WAAW;IACrB;;;OAGG;IACH,0CAA2B,CAAA;IAC3B,4DAA4D;IAC5D,0CAA2B,CAAA;AAC7B,CAAC,EARW,WAAW,KAAX,WAAW,QAQtB","sourcesContent":["import { PictureInPictureConfig } from './pictureInPictureConfig';\nimport { SubtitleViewConfig } from './subtitleViewConfig';\n\n/**\n * Configures the visual presentation and behaviour of the `PlayerView`.\n */\nexport interface PlayerViewConfig {\n /**\n * Configures the visual presentation and behaviour of the Bitmovin Player UI.\n * A {@link WebUiConfig} can be used to configure the default Bitmovin Player Web UI.\n *\n * Default is {@link WebUiConfig}.\n *\n * Limitations:\n * Configuring the `uiConfig` only has an effect if the {@link StyleConfig.userInterfaceType} is set to {@link UserInterfaceType.Bitmovin}.\n */\n uiConfig?: UiConfig;\n\n /**\n * Provides options to configure Picture in Picture playback.\n */\n pictureInPictureConfig?: PictureInPictureConfig;\n\n /**\n * When set to `true`, the first frame of the main content will not be rendered before playback starts. Default is `false`.\n * This configuration has no effect for the {@link UserInterfaceType.Subtitle} on iOS/tvOS.\n *\n * To reliably hide the first frame before a pre-roll ad, please ensure that you are using the {@link AdvertisingConfig} to schedule ads and not the {@link Player.scheduleAd} API call.\n */\n hideFirstFrame?: boolean;\n\n /**\n * Provides options to configure the subtitle view.\n */\n subtitleViewConfig?: SubtitleViewConfig;\n\n /**\n * Specify on which surface type the video should be rendered.\n *\n * See {@link https://developer.android.com/guide/topics/media/ui/playerview#surfacetype|Choosing a surface type}\n * for more information.\n *\n * Default is {@link SurfaceType.SurfaceView}.\n *\n * @remarks Platform: Android\n */\n surfaceType?: SurfaceType;\n}\n\n/**\n * Configures the visual presentation and behaviour of the Bitmovin Player UI.\n */\nexport type UiConfig = object;\n\n/**\n * Configures the visual presentation and behaviour of the Bitmovin Web UI.\n */\nexport interface WebUiConfig extends UiConfig {\n /**\n * Whether the Bitmovin Web UI will show playback speed selection options in the settings menu.\n * Default is `true`.\n */\n playbackSpeedSelectionEnabled?: boolean;\n /**\n * The UI variant to use for the Bitmovin Player Web UI.\n *\n * Default is {@link SmallScreenUi}\n */\n variant?: Variant;\n /**\n * Whether the WebView should be focused on initialization.\n *\n * By default this is enabled only for the TV UI variant, as it's needed there to\n * initiate spatial navigation using the remote control.\n *\n * @remarks Platform: Android\n */\n focusUiOnInitialization?: boolean;\n}\n\nexport abstract class Variant {\n /**\n * Specifies the function name that will be used to initialize the `UIManager`\n * for the Bitmovin Player Web UI.\n *\n * The function is called on the `window` object with the `Player` as the first argument and\n * the `UIConfig` as the second argument.\n *\n * Example:\n * When you added a new function or want to use a different function of our `UIFactory`,\n * you can specify the full qualifier name including namespaces.\n * e.g. `bitmovin.playerui.UIFactory.buildDefaultSmallScreenUI` for the SmallScreenUi.\n * @see UIFactory https://github.com/bitmovin/bitmovin-player-ui/blob/develop/src/ts/uifactory.ts#L60\n *\n * Notes:\n * - It's not necessary to use our `UIFactory`. Any static function can be specified.\n */\n constructor(public readonly uiManagerFactoryFunction: string) {}\n}\n\nexport class SmallScreenUi extends Variant {\n constructor() {\n super('bitmovin.playerui.UIFactory.buildDefaultSmallScreenUI');\n }\n}\n\nexport class TvUi extends Variant {\n constructor() {\n super('bitmovin.playerui.UIFactory.buildDefaultTvUI');\n }\n}\n\nexport class CustomUi extends Variant {}\n\n/**\n * The type of surface on which to render video.\n *\n * See {@link https://developer.android.com/guide/topics/media/ui/playerview#surfacetype|Choosing a surface type}\n * for more information.\n */\nexport enum SurfaceType {\n /**\n * SurfaceView generally causes lower battery consumption,\n * and has better handling for HDR and secure content.\n */\n SurfaceView = 'SurfaceView',\n /** TextureView is sometime needed for smooth animations. */\n TextureView = 'TextureView',\n}\n"]} \ No newline at end of file diff --git a/build/components/PlayerView/properties.d.ts b/build/components/PlayerView/properties.d.ts new file mode 100644 index 00000000..ff9e9db7 --- /dev/null +++ b/build/components/PlayerView/properties.d.ts @@ -0,0 +1,61 @@ +import { PlayerViewEvents } from './events'; +import { Player } from '../../player'; +import { FullscreenHandler, CustomMessageHandler } from '../../ui'; +import { ScalingMode } from '../../styleConfig'; +import { ViewStyle } from 'react-native'; +import { PlayerViewConfig } from './playerViewConfig'; +/** + * Base `PlayerView` component props. + * Used to establish common props between `NativePlayerView` and {@link PlayerView}. + */ +export interface BasePlayerViewProps { + ref?: React.RefObject; + /** + * The {@link FullscreenHandler} that is used by the {@link PlayerView} to control the fullscreen mode. + */ + fullscreenHandler?: FullscreenHandler; + /** + * The {@link CustomMessageHandler} that can be used to directly communicate with the embedded Bitmovin Web UI. + */ + customMessageHandler?: CustomMessageHandler; + /** + * Can be set to `true` to request fullscreen mode, or `false` to request exit of fullscreen mode. + * Should not be used to get the current fullscreen state. Use {@link PlayerViewEvents.onFullscreenEnter} and {@link PlayerViewEvents.onFullscreenExit} + * or the {@link FullscreenHandler.isFullscreenActive} property to get the current state. + * Using this property to change the fullscreen state, it is ensured that the embedded Player UI is also aware + * of potential fullscreen state changes. + * To use this property, a {@link FullscreenHandler} must be set. + */ + isFullscreenRequested?: boolean; + /** + * A value defining how the video is displayed within the parent container's bounds. + * Possible values are defined in {@link ScalingMode}. + */ + scalingMode?: ScalingMode; + /** + * Can be set to `true` to request Picture in Picture mode, or `false` to request exit of Picture in Picture mode. + * Should not be used to get the current Picture in Picture state. Use {@link PlayerViewEvents.onPictureInPictureEnter} and {@link PlayerViewEvents.onPictureInPictureExit}. + */ + isPictureInPictureRequested?: boolean; + /** + * Style of the {@link PlayerView}. + */ + style?: ViewStyle; + /** + * Configures the visual presentation and behaviour of the {@link PlayerView}. + * The value must not be altered after setting it initially. + */ + config?: PlayerViewConfig; +} +/** + * {@link PlayerView} component props. + */ +export interface PlayerViewProps extends BasePlayerViewProps, PlayerViewEvents { + viewRef?: React.MutableRefObject; + /** + * {@link Player} instance (generally returned from {@link usePlayer} hook) that will control + * and render audio/video inside the {@link PlayerView}. + */ + player: Player; +} +//# sourceMappingURL=properties.d.ts.map \ No newline at end of file diff --git a/build/components/PlayerView/properties.d.ts.map b/build/components/PlayerView/properties.d.ts.map new file mode 100644 index 00000000..563e6808 --- /dev/null +++ b/build/components/PlayerView/properties.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"properties.d.ts","sourceRoot":"","sources":["../../../src/components/PlayerView/properties.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC5B;;OAEG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IAEtC;;OAEG;IACH,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAE5C;;;;;;;OAOG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B;;;OAGG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;IAEtC;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAElB;;;OAGG;IACH,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,eAAgB,SAAQ,mBAAmB,EAAE,gBAAgB;IAC5E,OAAO,CAAC,EAAE,KAAK,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACvC;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB"} \ No newline at end of file diff --git a/build/components/PlayerView/properties.js b/build/components/PlayerView/properties.js new file mode 100644 index 00000000..94f8f708 --- /dev/null +++ b/build/components/PlayerView/properties.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=properties.js.map \ No newline at end of file diff --git a/build/components/PlayerView/properties.js.map b/build/components/PlayerView/properties.js.map new file mode 100644 index 00000000..ad5fa308 --- /dev/null +++ b/build/components/PlayerView/properties.js.map @@ -0,0 +1 @@ +{"version":3,"file":"properties.js","sourceRoot":"","sources":["../../../src/components/PlayerView/properties.ts"],"names":[],"mappings":"","sourcesContent":["import { PlayerViewEvents } from './events';\nimport { Player } from '../../player';\nimport { FullscreenHandler, CustomMessageHandler } from '../../ui';\nimport { ScalingMode } from '../../styleConfig';\nimport { ViewStyle } from 'react-native';\nimport { PlayerViewConfig } from './playerViewConfig';\n\n/**\n * Base `PlayerView` component props.\n * Used to establish common props between `NativePlayerView` and {@link PlayerView}.\n */\nexport interface BasePlayerViewProps {\n ref?: React.RefObject;\n /**\n * The {@link FullscreenHandler} that is used by the {@link PlayerView} to control the fullscreen mode.\n */\n fullscreenHandler?: FullscreenHandler;\n\n /**\n * The {@link CustomMessageHandler} that can be used to directly communicate with the embedded Bitmovin Web UI.\n */\n customMessageHandler?: CustomMessageHandler;\n\n /**\n * Can be set to `true` to request fullscreen mode, or `false` to request exit of fullscreen mode.\n * Should not be used to get the current fullscreen state. Use {@link PlayerViewEvents.onFullscreenEnter} and {@link PlayerViewEvents.onFullscreenExit}\n * or the {@link FullscreenHandler.isFullscreenActive} property to get the current state.\n * Using this property to change the fullscreen state, it is ensured that the embedded Player UI is also aware\n * of potential fullscreen state changes.\n * To use this property, a {@link FullscreenHandler} must be set.\n */\n isFullscreenRequested?: boolean;\n\n /**\n * A value defining how the video is displayed within the parent container's bounds.\n * Possible values are defined in {@link ScalingMode}.\n */\n scalingMode?: ScalingMode;\n\n /**\n * Can be set to `true` to request Picture in Picture mode, or `false` to request exit of Picture in Picture mode.\n * Should not be used to get the current Picture in Picture state. Use {@link PlayerViewEvents.onPictureInPictureEnter} and {@link PlayerViewEvents.onPictureInPictureExit}.\n */\n isPictureInPictureRequested?: boolean;\n\n /**\n * Style of the {@link PlayerView}.\n */\n style?: ViewStyle;\n\n /**\n * Configures the visual presentation and behaviour of the {@link PlayerView}.\n * The value must not be altered after setting it initially.\n */\n config?: PlayerViewConfig;\n}\n\n/**\n * {@link PlayerView} component props.\n */\nexport interface PlayerViewProps extends BasePlayerViewProps, PlayerViewEvents {\n viewRef?: React.MutableRefObject;\n /**\n * {@link Player} instance (generally returned from {@link usePlayer} hook) that will control\n * and render audio/video inside the {@link PlayerView}.\n */\n player: Player;\n}\n"]} \ No newline at end of file diff --git a/build/components/PlayerView/subtitleViewConfig.d.ts b/build/components/PlayerView/subtitleViewConfig.d.ts new file mode 100644 index 00000000..9b33e9f9 --- /dev/null +++ b/build/components/PlayerView/subtitleViewConfig.d.ts @@ -0,0 +1,7 @@ +export interface SubtitleViewConfig { + paddingLeft?: number; + paddingTop?: number; + paddingRight?: number; + paddingBottom?: number; +} +//# sourceMappingURL=subtitleViewConfig.d.ts.map \ No newline at end of file diff --git a/build/components/PlayerView/subtitleViewConfig.d.ts.map b/build/components/PlayerView/subtitleViewConfig.d.ts.map new file mode 100644 index 00000000..5ab4a8e2 --- /dev/null +++ b/build/components/PlayerView/subtitleViewConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"subtitleViewConfig.d.ts","sourceRoot":"","sources":["../../../src/components/PlayerView/subtitleViewConfig.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB"} \ No newline at end of file diff --git a/build/components/PlayerView/subtitleViewConfig.js b/build/components/PlayerView/subtitleViewConfig.js new file mode 100644 index 00000000..4ff95e23 --- /dev/null +++ b/build/components/PlayerView/subtitleViewConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=subtitleViewConfig.js.map \ No newline at end of file diff --git a/build/components/PlayerView/subtitleViewConfig.js.map b/build/components/PlayerView/subtitleViewConfig.js.map new file mode 100644 index 00000000..f3edd630 --- /dev/null +++ b/build/components/PlayerView/subtitleViewConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"subtitleViewConfig.js","sourceRoot":"","sources":["../../../src/components/PlayerView/subtitleViewConfig.ts"],"names":[],"mappings":"","sourcesContent":["export interface SubtitleViewConfig {\n paddingLeft?: number;\n paddingTop?: number;\n paddingRight?: number;\n paddingBottom?: number;\n}\n"]} \ No newline at end of file diff --git a/build/components/index.d.ts b/build/components/index.d.ts new file mode 100644 index 00000000..37c7b7ea --- /dev/null +++ b/build/components/index.d.ts @@ -0,0 +1,6 @@ +export * from './PlayerView'; +export * from './PlayerView/pictureInPictureConfig'; +export * from './PlayerView/playerViewConfig'; +export * from './PlayerView/properties'; +export * from './PlayerView/events'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/build/components/index.d.ts.map b/build/components/index.d.ts.map new file mode 100644 index 00000000..bba05337 --- /dev/null +++ b/build/components/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,qCAAqC,CAAC;AACpD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC"} \ No newline at end of file diff --git a/build/components/index.js b/build/components/index.js new file mode 100644 index 00000000..4fd9dccd --- /dev/null +++ b/build/components/index.js @@ -0,0 +1,6 @@ +export * from './PlayerView'; +export * from './PlayerView/pictureInPictureConfig'; +export * from './PlayerView/playerViewConfig'; +export * from './PlayerView/properties'; +export * from './PlayerView/events'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/build/components/index.js.map b/build/components/index.js.map new file mode 100644 index 00000000..77f8877e --- /dev/null +++ b/build/components/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,qCAAqC,CAAC;AACpD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC","sourcesContent":["export * from './PlayerView';\nexport * from './PlayerView/pictureInPictureConfig';\nexport * from './PlayerView/playerViewConfig';\nexport * from './PlayerView/properties';\nexport * from './PlayerView/events';\n"]} \ No newline at end of file diff --git a/build/debug.d.ts b/build/debug.d.ts new file mode 100644 index 00000000..1c6883a4 --- /dev/null +++ b/build/debug.d.ts @@ -0,0 +1,46 @@ +/** + * Global debug configuration for all Bitmovin components. + */ +export declare class DebugConfig { + private static _isDebugEnabled; + /** + * Retrieves the current debug logging state. + * + * @returns `true` if debug logging is enabled, otherwise `false`. + */ + static get isDebugLoggingEnabled(): boolean; + /** + * Enables or disables global debug logging for all Bitmovin components. + * + * Debug logging provides detailed information primarily for debugging purposes, + * helping to diagnose problems and trace the flow of execution within the Player. + * + * ### Warning: + * This option **should not be enabled in production** as it may log sensitive or confidential + * information to the console. + * + * ## Platform-Specific Logging Behavior + * --- + * - **iOS:** logs are printed using `NSLog` at the verbose log level. + * - **Android:** logs are printed using `android.util.Log` with the following tags: + * - `BitmovinPlayer` + * - `BitmovinPlayerView` + * - `BitmovinOffline` + * - `BitmovinSource` + * - `BitmovinExoPlayer` + * + * ## Limitations + * --- + * **Android** + * - This flag **must** be set **before** creating any Bitmovin component to take effect. + * + * ## Usage Notes + * --- + * - We recommend setting this flag during your app's initialization phase, such as in the + * application's entry point (e.g. `App.tsx`). + * + * @defaultValue `false` + */ + static setDebugLoggingEnabled(value: boolean): Promise; +} +//# sourceMappingURL=debug.d.ts.map \ No newline at end of file diff --git a/build/debug.d.ts.map b/build/debug.d.ts.map new file mode 100644 index 00000000..d6ff3fe1 --- /dev/null +++ b/build/debug.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"debug.d.ts","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAC,eAAe,CAAS;IAEvC;;;;OAIG;IACH,MAAM,KAAK,qBAAqB,IAAI,OAAO,CAE1C;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;WACU,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAInE"} \ No newline at end of file diff --git a/build/debug.js b/build/debug.js new file mode 100644 index 00000000..53e505cf --- /dev/null +++ b/build/debug.js @@ -0,0 +1,52 @@ +import DebugModule from './modules/DebugModule'; +/** + * Global debug configuration for all Bitmovin components. + */ +export class DebugConfig { + static _isDebugEnabled = false; + /** + * Retrieves the current debug logging state. + * + * @returns `true` if debug logging is enabled, otherwise `false`. + */ + static get isDebugLoggingEnabled() { + return DebugConfig._isDebugEnabled; + } + /** + * Enables or disables global debug logging for all Bitmovin components. + * + * Debug logging provides detailed information primarily for debugging purposes, + * helping to diagnose problems and trace the flow of execution within the Player. + * + * ### Warning: + * This option **should not be enabled in production** as it may log sensitive or confidential + * information to the console. + * + * ## Platform-Specific Logging Behavior + * --- + * - **iOS:** logs are printed using `NSLog` at the verbose log level. + * - **Android:** logs are printed using `android.util.Log` with the following tags: + * - `BitmovinPlayer` + * - `BitmovinPlayerView` + * - `BitmovinOffline` + * - `BitmovinSource` + * - `BitmovinExoPlayer` + * + * ## Limitations + * --- + * **Android** + * - This flag **must** be set **before** creating any Bitmovin component to take effect. + * + * ## Usage Notes + * --- + * - We recommend setting this flag during your app's initialization phase, such as in the + * application's entry point (e.g. `App.tsx`). + * + * @defaultValue `false` + */ + static async setDebugLoggingEnabled(value) { + DebugConfig._isDebugEnabled = value; + await DebugModule.setDebugLoggingEnabled(value); + } +} +//# sourceMappingURL=debug.js.map \ No newline at end of file diff --git a/build/debug.js.map b/build/debug.js.map new file mode 100644 index 00000000..aa762dfa --- /dev/null +++ b/build/debug.js.map @@ -0,0 +1 @@ +{"version":3,"file":"debug.js","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAEhD;;GAEG;AACH,MAAM,OAAO,WAAW;IACd,MAAM,CAAC,eAAe,GAAG,KAAK,CAAC;IAEvC;;;;OAIG;IACH,MAAM,KAAK,qBAAqB;QAC9B,OAAO,WAAW,CAAC,eAAe,CAAC;IACrC,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,KAAc;QAChD,WAAW,CAAC,eAAe,GAAG,KAAK,CAAC;QACpC,MAAM,WAAW,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC","sourcesContent":["import DebugModule from './modules/DebugModule';\n\n/**\n * Global debug configuration for all Bitmovin components.\n */\nexport class DebugConfig {\n private static _isDebugEnabled = false;\n\n /**\n * Retrieves the current debug logging state.\n *\n * @returns `true` if debug logging is enabled, otherwise `false`.\n */\n static get isDebugLoggingEnabled(): boolean {\n return DebugConfig._isDebugEnabled;\n }\n\n /**\n * Enables or disables global debug logging for all Bitmovin components.\n *\n * Debug logging provides detailed information primarily for debugging purposes,\n * helping to diagnose problems and trace the flow of execution within the Player.\n *\n * ### Warning:\n * This option **should not be enabled in production** as it may log sensitive or confidential\n * information to the console.\n *\n * ## Platform-Specific Logging Behavior\n * ---\n * - **iOS:** logs are printed using `NSLog` at the verbose log level.\n * - **Android:** logs are printed using `android.util.Log` with the following tags:\n * - `BitmovinPlayer`\n * - `BitmovinPlayerView`\n * - `BitmovinOffline`\n * - `BitmovinSource`\n * - `BitmovinExoPlayer`\n *\n * ## Limitations\n * ---\n * **Android**\n * - This flag **must** be set **before** creating any Bitmovin component to take effect.\n *\n * ## Usage Notes\n * ---\n * - We recommend setting this flag during your app's initialization phase, such as in the\n * application's entry point (e.g. `App.tsx`).\n *\n * @defaultValue `false`\n */\n static async setDebugLoggingEnabled(value: boolean): Promise {\n DebugConfig._isDebugEnabled = value;\n await DebugModule.setDebugLoggingEnabled(value);\n }\n}\n"]} \ No newline at end of file diff --git a/build/decoder/decoderConfig.d.ts b/build/decoder/decoderConfig.d.ts new file mode 100644 index 00000000..13eb741c --- /dev/null +++ b/build/decoder/decoderConfig.d.ts @@ -0,0 +1,96 @@ +import { NativeInstanceConfig } from '../nativeInstance'; +/** + * Configures the playback behaviour of the player. + * + * @remarks Platform: Android + */ +export interface DecoderConfig extends NativeInstanceConfig { + /** + * A callback interface for sorting and filtering decoders based on priority. + * + * This callback is invoked when the player selects a decoder, providing the {@link DecoderContext} + * and a list of available {@link MediaCodecInfo} objects. The list is initially ordered by + * the default priority in which decoders will be attempted. + * + * The callback should return a reordered or filtered list of {@link MediaCodecInfo} objects + * that determines the selection priority. + * + * ## Example Usage + * + * ### Prefer a specific decoder for main content video playback + * The following example prioritizes a specific decoder for non-ad video playback: + * ```ts + * const decoderPriorityProvider: DecoderPriorityProvider = { + * overrideDecodersPriority: (context: DecoderContext, preferredDecoders: MediaCodecInfo[]): MediaCodecInfo[] => { + * if (!context.isAd && context.mediaType === DecoderContextMediaType.VIDEO) { + * // Prioritize a specific decoder + * return preferredDecoders.sort((a, b) => { + * const aAsNumber = a.name.startsWith("OMX.google.") ? 1 : 2 + * const bAsNumber = b.name.startsWith("OMX.google.") ? 1 : 2 + * return aAsNumber - bAsNumber + * }) + * } + * return preferredDecoders + * } + * } + * ``` + * + * ### Prefer software decoders for ads playback + * The following example prioritizes software decoders over hardware decoders for ad playback: + * ```ts + * const decoderPriorityProvider: DecoderPriorityProvider = { + * overrideDecodersPriority: (context: DecoderContext, preferredDecoders: MediaCodecInfo[]): MediaCodecInfo[] => { + * if (context.isAd) { + * // Prioritize a specific decoder + * return preferredDecoders.sort((a, b) => { + * const aAsNumber = a.isSoftware ? 1 : 2 + * const bAsNumber = b.isSoftware ? 1 : 2 + * return aAsNumber - bAsNumber + * }) + * } + * return preferredDecoders + * } + * } + * ``` + * + * ### Disable software fallback for video playback + * The following example disables software decoders for non-ad video playback: + * ```ts + * const decoderPriorityProvider: DecoderPriorityProvider = { + * overrideDecodersPriority: (context: DecoderContext, preferredDecoders: MediaCodecInfo[]): MediaCodecInfo[] => { + * if (!context.isAd && context.mediaType === DecoderContextMediaType.VIDEO) { + * // Prioritize a specific decoder + * return preferredDecoders.filter((info) => { + * return !info.isSoftware + * }) + * } + * return preferredDecoders + * } + * } + * ``` + */ + decoderPriorityProvider?: DecoderPriorityProvider | null; +} +/** + * Can be set on the `DecoderConfig.decoderPriorityProvider` to override the default decoder selection logic. + * See {@link DecoderConfig#decoderPriorityProvider} for more details + * + * @remarks Platform: Android + * */ +export interface DecoderPriorityProvider { + overrideDecodersPriority: (context: DecoderContext, preferredDecoders: MediaCodecInfo[]) => MediaCodecInfo[]; +} +/** The context in which a new decoder is chosen. */ +export interface DecoderContext { + mediaType: DecoderContextMediaType; + isAd: boolean; +} +export interface MediaCodecInfo { + name: string; + isSoftware: boolean; +} +export declare enum DecoderContextMediaType { + AUDIO = "Audio", + VIDEO = "Video" +} +//# sourceMappingURL=decoderConfig.d.ts.map \ No newline at end of file diff --git a/build/decoder/decoderConfig.d.ts.map b/build/decoder/decoderConfig.d.ts.map new file mode 100644 index 00000000..65800fe5 --- /dev/null +++ b/build/decoder/decoderConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"decoderConfig.d.ts","sourceRoot":"","sources":["../../src/decoder/decoderConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD;;;;GAIG;AACH,MAAM,WAAW,aAAc,SAAQ,oBAAoB;IACzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+DG;IACH,uBAAuB,CAAC,EAAE,uBAAuB,GAAG,IAAI,CAAC;CAC1D;AAED;;;;;KAKK;AACL,MAAM,WAAW,uBAAuB;IACtC,wBAAwB,EAAE,CACxB,OAAO,EAAE,cAAc,EACvB,iBAAiB,EAAE,cAAc,EAAE,KAChC,cAAc,EAAE,CAAC;CACvB;AAED,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,uBAAuB,CAAC;IACnC,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,oBAAY,uBAAuB;IACjC,KAAK,UAAU;IACf,KAAK,UAAU;CAChB"} \ No newline at end of file diff --git a/build/decoder/decoderConfig.js b/build/decoder/decoderConfig.js new file mode 100644 index 00000000..6419f2c9 --- /dev/null +++ b/build/decoder/decoderConfig.js @@ -0,0 +1,6 @@ +export var DecoderContextMediaType; +(function (DecoderContextMediaType) { + DecoderContextMediaType["AUDIO"] = "Audio"; + DecoderContextMediaType["VIDEO"] = "Video"; +})(DecoderContextMediaType || (DecoderContextMediaType = {})); +//# sourceMappingURL=decoderConfig.js.map \ No newline at end of file diff --git a/build/decoder/decoderConfig.js.map b/build/decoder/decoderConfig.js.map new file mode 100644 index 00000000..145adcef --- /dev/null +++ b/build/decoder/decoderConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"decoderConfig.js","sourceRoot":"","sources":["../../src/decoder/decoderConfig.ts"],"names":[],"mappings":"AAmGA,MAAM,CAAN,IAAY,uBAGX;AAHD,WAAY,uBAAuB;IACjC,0CAAe,CAAA;IACf,0CAAe,CAAA;AACjB,CAAC,EAHW,uBAAuB,KAAvB,uBAAuB,QAGlC","sourcesContent":["import { NativeInstanceConfig } from '../nativeInstance';\n\n/**\n * Configures the playback behaviour of the player.\n *\n * @remarks Platform: Android\n */\nexport interface DecoderConfig extends NativeInstanceConfig {\n /**\n * A callback interface for sorting and filtering decoders based on priority.\n *\n * This callback is invoked when the player selects a decoder, providing the {@link DecoderContext}\n * and a list of available {@link MediaCodecInfo} objects. The list is initially ordered by\n * the default priority in which decoders will be attempted.\n *\n * The callback should return a reordered or filtered list of {@link MediaCodecInfo} objects\n * that determines the selection priority.\n *\n * ## Example Usage\n *\n * ### Prefer a specific decoder for main content video playback\n * The following example prioritizes a specific decoder for non-ad video playback:\n * ```ts\n * const decoderPriorityProvider: DecoderPriorityProvider = {\n * overrideDecodersPriority: (context: DecoderContext, preferredDecoders: MediaCodecInfo[]): MediaCodecInfo[] => {\n * if (!context.isAd && context.mediaType === DecoderContextMediaType.VIDEO) {\n * // Prioritize a specific decoder\n * return preferredDecoders.sort((a, b) => {\n * const aAsNumber = a.name.startsWith(\"OMX.google.\") ? 1 : 2\n * const bAsNumber = b.name.startsWith(\"OMX.google.\") ? 1 : 2\n * return aAsNumber - bAsNumber\n * })\n * }\n * return preferredDecoders\n * }\n * }\n * ```\n *\n * ### Prefer software decoders for ads playback\n * The following example prioritizes software decoders over hardware decoders for ad playback:\n * ```ts\n * const decoderPriorityProvider: DecoderPriorityProvider = {\n * overrideDecodersPriority: (context: DecoderContext, preferredDecoders: MediaCodecInfo[]): MediaCodecInfo[] => {\n * if (context.isAd) {\n * // Prioritize a specific decoder\n * return preferredDecoders.sort((a, b) => {\n * const aAsNumber = a.isSoftware ? 1 : 2\n * const bAsNumber = b.isSoftware ? 1 : 2\n * return aAsNumber - bAsNumber\n * })\n * }\n * return preferredDecoders\n * }\n * }\n * ```\n *\n * ### Disable software fallback for video playback\n * The following example disables software decoders for non-ad video playback:\n * ```ts\n * const decoderPriorityProvider: DecoderPriorityProvider = {\n * overrideDecodersPriority: (context: DecoderContext, preferredDecoders: MediaCodecInfo[]): MediaCodecInfo[] => {\n * if (!context.isAd && context.mediaType === DecoderContextMediaType.VIDEO) {\n * // Prioritize a specific decoder\n * return preferredDecoders.filter((info) => {\n * return !info.isSoftware\n * })\n * }\n * return preferredDecoders\n * }\n * }\n * ```\n */\n decoderPriorityProvider?: DecoderPriorityProvider | null;\n}\n\n/**\n * Can be set on the `DecoderConfig.decoderPriorityProvider` to override the default decoder selection logic.\n * See {@link DecoderConfig#decoderPriorityProvider} for more details\n *\n * @remarks Platform: Android\n * */\nexport interface DecoderPriorityProvider {\n overrideDecodersPriority: (\n context: DecoderContext,\n preferredDecoders: MediaCodecInfo[]\n ) => MediaCodecInfo[];\n}\n\n/** The context in which a new decoder is chosen. */\nexport interface DecoderContext {\n mediaType: DecoderContextMediaType;\n isAd: boolean;\n}\n\nexport interface MediaCodecInfo {\n name: string;\n isSoftware: boolean;\n}\n\nexport enum DecoderContextMediaType {\n AUDIO = 'Audio',\n VIDEO = 'Video',\n}\n"]} \ No newline at end of file diff --git a/build/decoder/decoderConfigModule.d.ts b/build/decoder/decoderConfigModule.d.ts new file mode 100644 index 00000000..956f4525 --- /dev/null +++ b/build/decoder/decoderConfigModule.d.ts @@ -0,0 +1,14 @@ +export type DecoderConfigModuleEvents = { + onOverrideDecodersPriority: ({ nativeId, context, preferredDecoders, }: { + nativeId: string; + context: any; + preferredDecoders: any[]; + }) => void; +}; +/** + * Expo-based DecoderConfigModule implementation. + * Android-only module that gracefully handles iOS by providing no-op implementations. + */ +declare let DecoderConfigModuleInstance: any; +export default DecoderConfigModuleInstance; +//# sourceMappingURL=decoderConfigModule.d.ts.map \ No newline at end of file diff --git a/build/decoder/decoderConfigModule.d.ts.map b/build/decoder/decoderConfigModule.d.ts.map new file mode 100644 index 00000000..84e60c15 --- /dev/null +++ b/build/decoder/decoderConfigModule.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"decoderConfigModule.d.ts","sourceRoot":"","sources":["../../src/decoder/decoderConfigModule.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,yBAAyB,GAAG;IACtC,0BAA0B,EAAE,CAAC,EAC3B,QAAQ,EACR,OAAO,EACP,iBAAiB,GAClB,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,GAAG,CAAC;QACb,iBAAiB,EAAE,GAAG,EAAE,CAAC;KAC1B,KAAK,IAAI,CAAC;CACZ,CAAC;AAkBF;;;GAGG;AACH,QAAA,IAAI,2BAA2B,EAAE,GAAG,CAAC;AA0BrC,eAAe,2BAA2B,CAAC"} \ No newline at end of file diff --git a/build/decoder/decoderConfigModule.js b/build/decoder/decoderConfigModule.js new file mode 100644 index 00000000..d4202295 --- /dev/null +++ b/build/decoder/decoderConfigModule.js @@ -0,0 +1,31 @@ +import { requireNativeModule } from 'expo-modules-core'; +import { Platform } from 'react-native'; +/** + * Expo-based DecoderConfigModule implementation. + * Android-only module that gracefully handles iOS by providing no-op implementations. + */ +let DecoderConfigModuleInstance; +if (Platform.OS === 'android') { + DecoderConfigModuleInstance = requireNativeModule('DecoderConfigModule'); +} +else { + // iOS graceful fallback - provide no-op implementations + DecoderConfigModuleInstance = { + initializeWithConfig: async () => { + // No-op on iOS + }, + overrideDecoderPriorityProviderComplete: async () => { + // No-op on iOS + }, + destroy: async () => { + // No-op on iOS + }, + addListener: () => ({ remove: () => { } }), + removeListener: () => { }, + removeAllListeners: () => { }, + emit: () => { }, + listenerCount: () => 0, + }; +} +export default DecoderConfigModuleInstance; +//# sourceMappingURL=decoderConfigModule.js.map \ No newline at end of file diff --git a/build/decoder/decoderConfigModule.js.map b/build/decoder/decoderConfigModule.js.map new file mode 100644 index 00000000..aaedf9e6 --- /dev/null +++ b/build/decoder/decoderConfigModule.js.map @@ -0,0 +1 @@ +{"version":3,"file":"decoderConfigModule.js","sourceRoot":"","sources":["../../src/decoder/decoderConfigModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AA8BxC;;;GAGG;AACH,IAAI,2BAAgC,CAAC;AAErC,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;IAC9B,2BAA2B,GAAG,mBAAmB,CAC/C,qBAAqB,CACtB,CAAC;AACJ,CAAC;KAAM,CAAC;IACN,wDAAwD;IACxD,2BAA2B,GAAG;QAC5B,oBAAoB,EAAE,KAAK,IAAI,EAAE;YAC/B,eAAe;QACjB,CAAC;QACD,uCAAuC,EAAE,KAAK,IAAI,EAAE;YAClD,eAAe;QACjB,CAAC;QACD,OAAO,EAAE,KAAK,IAAI,EAAE;YAClB,eAAe;QACjB,CAAC;QACD,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC;QACzC,cAAc,EAAE,GAAG,EAAE,GAAE,CAAC;QACxB,kBAAkB,EAAE,GAAG,EAAE,GAAE,CAAC;QAC5B,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;QACd,aAAa,EAAE,GAAG,EAAE,CAAC,CAAC;KACvB,CAAC;AACJ,CAAC;AAED,eAAe,2BAA2B,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo-modules-core';\nimport { Platform } from 'react-native';\n\nexport type DecoderConfigModuleEvents = {\n onOverrideDecodersPriority: ({\n nativeId,\n context,\n preferredDecoders,\n }: {\n nativeId: string;\n context: any;\n preferredDecoders: any[];\n }) => void;\n};\n\n/**\n * Native DecoderConfigModule using Expo modules API.\n * Android-only module for decoder configuration.\n */\ndeclare class DecoderConfigModule extends NativeModule {\n initializeWithConfig(\n nativeId: string,\n config: Record\n ): Promise;\n overrideDecoderPriorityProviderComplete(\n nativeId: string,\n response: any[]\n ): Promise;\n destroy(nativeId: string): Promise;\n}\n\n/**\n * Expo-based DecoderConfigModule implementation.\n * Android-only module that gracefully handles iOS by providing no-op implementations.\n */\nlet DecoderConfigModuleInstance: any;\n\nif (Platform.OS === 'android') {\n DecoderConfigModuleInstance = requireNativeModule(\n 'DecoderConfigModule'\n );\n} else {\n // iOS graceful fallback - provide no-op implementations\n DecoderConfigModuleInstance = {\n initializeWithConfig: async () => {\n // No-op on iOS\n },\n overrideDecoderPriorityProviderComplete: async () => {\n // No-op on iOS\n },\n destroy: async () => {\n // No-op on iOS\n },\n addListener: () => ({ remove: () => {} }),\n removeListener: () => {},\n removeAllListeners: () => {},\n emit: () => {},\n listenerCount: () => 0,\n };\n}\n\nexport default DecoderConfigModuleInstance;\n"]} \ No newline at end of file diff --git a/build/decoder/index.d.ts b/build/decoder/index.d.ts new file mode 100644 index 00000000..71a08b43 --- /dev/null +++ b/build/decoder/index.d.ts @@ -0,0 +1,26 @@ +import { DecoderConfig } from './decoderConfig'; +import NativeInstance from '../nativeInstance'; +/** + * Takes care of JS/Native communication for a `DecoderConfig`. + */ +export declare class DecoderConfigBridge extends NativeInstance { + /** + * Whether this object's native instance has been created. + */ + isInitialized: boolean; + /** + * Whether this object's native instance has been disposed. + */ + isDestroyed: boolean; + private onOverrideDecodersPrioritySubscription?; + initialize(): void; + /** + * Destroys the native `DecoderConfig` + */ + destroy(): void; + /** + * Called by native code, when the decoder priority should be evaluated. + */ + private overrideDecodersPriority; +} +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/build/decoder/index.d.ts.map b/build/decoder/index.d.ts.map new file mode 100644 index 00000000..941a14ee --- /dev/null +++ b/build/decoder/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/decoder/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAkC,MAAM,iBAAiB,CAAC;AAChF,OAAO,cAAc,MAAM,mBAAmB,CAAC;AAG/C;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,cAAc,CAAC,aAAa,CAAC;IACpE;;OAEG;IACH,aAAa,UAAS;IACtB;;OAEG;IACH,WAAW,UAAS;IAEpB,OAAO,CAAC,sCAAsC,CAAC,CAAoB;IAEnE,UAAU;IA+BV;;OAEG;IACH,OAAO;IASP;;OAEG;IACH,OAAO,CAAC,wBAAwB;CAejC"} \ No newline at end of file diff --git a/build/decoder/index.js b/build/decoder/index.js new file mode 100644 index 00000000..130f4627 --- /dev/null +++ b/build/decoder/index.js @@ -0,0 +1,50 @@ +import NativeInstance from '../nativeInstance'; +import DecoderConfigModule from './decoderConfigModule'; +/** + * Takes care of JS/Native communication for a `DecoderConfig`. + */ +export class DecoderConfigBridge extends NativeInstance { + /** + * Whether this object's native instance has been created. + */ + isInitialized = false; + /** + * Whether this object's native instance has been disposed. + */ + isDestroyed = false; + onOverrideDecodersPrioritySubscription; + initialize() { + if (!this.isInitialized) { + // Set up event listener for decoder priority override + this.onOverrideDecodersPrioritySubscription = + DecoderConfigModule.addListener('onOverrideDecodersPriority', ({ nativeId, context, preferredDecoders, }) => { + if (nativeId !== this.nativeId) { + return; + } + this.overrideDecodersPriority(context, preferredDecoders); + }); + // Create native configuration object. + DecoderConfigModule.initializeWithConfig(this.nativeId, this.config || {}); + this.isInitialized = true; + } + } + /** + * Destroys the native `DecoderConfig` + */ + destroy() { + if (!this.isDestroyed) { + DecoderConfigModule.destroy(this.nativeId); + this.onOverrideDecodersPrioritySubscription?.remove(); + this.onOverrideDecodersPrioritySubscription = undefined; + this.isDestroyed = true; + } + } + /** + * Called by native code, when the decoder priority should be evaluated. + */ + overrideDecodersPriority(context, preferredDecoders) { + const orderedPriority = this.config?.decoderPriorityProvider?.overrideDecodersPriority(context, preferredDecoders) ?? preferredDecoders; + DecoderConfigModule.overrideDecoderPriorityProviderComplete(this.nativeId, orderedPriority); + } +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/build/decoder/index.js.map b/build/decoder/index.js.map new file mode 100644 index 00000000..0f09a2c0 --- /dev/null +++ b/build/decoder/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/decoder/index.ts"],"names":[],"mappings":"AAEA,OAAO,cAAc,MAAM,mBAAmB,CAAC;AAC/C,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AAExD;;GAEG;AACH,MAAM,OAAO,mBAAoB,SAAQ,cAA6B;IACpE;;OAEG;IACH,aAAa,GAAG,KAAK,CAAC;IACtB;;OAEG;IACH,WAAW,GAAG,KAAK,CAAC;IAEZ,sCAAsC,CAAqB;IAEnE,UAAU;QACR,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,sDAAsD;YACtD,IAAI,CAAC,sCAAsC;gBACzC,mBAAmB,CAAC,WAAW,CAC7B,4BAA4B,EAC5B,CAAC,EACC,QAAQ,EACR,OAAO,EACP,iBAAiB,GAKlB,EAAE,EAAE;oBACH,IAAI,QAAQ,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;wBAC/B,OAAO;oBACT,CAAC;oBACD,IAAI,CAAC,wBAAwB,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;gBAC5D,CAAC,CACF,CAAC;YAEJ,sCAAsC;YACtC,mBAAmB,CAAC,oBAAoB,CACtC,IAAI,CAAC,QAAQ,EACb,IAAI,CAAC,MAAM,IAAI,EAAE,CAClB,CAAC;YACF,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC5B,CAAC;IACH,CAAC;IAED;;OAEG;IACH,OAAO;QACL,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3C,IAAI,CAAC,sCAAsC,EAAE,MAAM,EAAE,CAAC;YACtD,IAAI,CAAC,sCAAsC,GAAG,SAAS,CAAC;YACxD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;IAED;;OAEG;IACK,wBAAwB,CAC9B,OAAuB,EACvB,iBAAmC;QAEnC,MAAM,eAAe,GACnB,IAAI,CAAC,MAAM,EAAE,uBAAuB,EAAE,wBAAwB,CAC5D,OAAO,EACP,iBAAiB,CAClB,IAAI,iBAAiB,CAAC;QAEzB,mBAAmB,CAAC,uCAAuC,CACzD,IAAI,CAAC,QAAQ,EACb,eAAe,CAChB,CAAC;IACJ,CAAC;CACF","sourcesContent":["import { EventSubscription } from 'expo-modules-core';\nimport { DecoderConfig, DecoderContext, MediaCodecInfo } from './decoderConfig';\nimport NativeInstance from '../nativeInstance';\nimport DecoderConfigModule from './decoderConfigModule';\n\n/**\n * Takes care of JS/Native communication for a `DecoderConfig`.\n */\nexport class DecoderConfigBridge extends NativeInstance {\n /**\n * Whether this object's native instance has been created.\n */\n isInitialized = false;\n /**\n * Whether this object's native instance has been disposed.\n */\n isDestroyed = false;\n\n private onOverrideDecodersPrioritySubscription?: EventSubscription;\n\n initialize() {\n if (!this.isInitialized) {\n // Set up event listener for decoder priority override\n this.onOverrideDecodersPrioritySubscription =\n DecoderConfigModule.addListener(\n 'onOverrideDecodersPriority',\n ({\n nativeId,\n context,\n preferredDecoders,\n }: {\n nativeId: string;\n context: DecoderContext;\n preferredDecoders: MediaCodecInfo[];\n }) => {\n if (nativeId !== this.nativeId) {\n return;\n }\n this.overrideDecodersPriority(context, preferredDecoders);\n }\n );\n\n // Create native configuration object.\n DecoderConfigModule.initializeWithConfig(\n this.nativeId,\n this.config || {}\n );\n this.isInitialized = true;\n }\n }\n\n /**\n * Destroys the native `DecoderConfig`\n */\n destroy() {\n if (!this.isDestroyed) {\n DecoderConfigModule.destroy(this.nativeId);\n this.onOverrideDecodersPrioritySubscription?.remove();\n this.onOverrideDecodersPrioritySubscription = undefined;\n this.isDestroyed = true;\n }\n }\n\n /**\n * Called by native code, when the decoder priority should be evaluated.\n */\n private overrideDecodersPriority(\n context: DecoderContext,\n preferredDecoders: MediaCodecInfo[]\n ): void {\n const orderedPriority =\n this.config?.decoderPriorityProvider?.overrideDecodersPriority(\n context,\n preferredDecoders\n ) ?? preferredDecoders;\n\n DecoderConfigModule.overrideDecoderPriorityProviderComplete(\n this.nativeId,\n orderedPriority\n );\n }\n}\n"]} \ No newline at end of file diff --git a/build/drm/drmModule.d.ts b/build/drm/drmModule.d.ts new file mode 100644 index 00000000..3199eade --- /dev/null +++ b/build/drm/drmModule.d.ts @@ -0,0 +1,55 @@ +import { NativeModule } from 'expo-modules-core'; +import { DrmConfig } from './index'; +export type DrmModuleEvents = { + onPrepareCertificate: ({ nativeId, id, certificate, }: { + nativeId: string; + id: string; + certificate: string; + }) => void; + onPrepareMessage: ({ nativeId, id, data, message, assetId, }: { + nativeId: string; + id: string; + data?: string; + message?: string; + assetId?: string; + }) => void; + onPrepareSyncMessage: ({ nativeId, id, syncMessage, assetId, }: { + nativeId: string; + id: string; + syncMessage: string; + assetId: string; + }) => void; + onPrepareLicense: ({ nativeId, id, data, license, }: { + nativeId: string; + id: string; + data?: string; + license?: string; + }) => void; + onPrepareLicenseServerUrl: ({ nativeId, id, licenseServerUrl, }: { + nativeId: string; + id: string; + licenseServerUrl: string; + }) => void; + onPrepareContentId: ({ nativeId, id, contentId, }: { + nativeId: string; + id: string; + contentId: string; + }) => void; +}; +/** + * Native DrmModule using Expo modules API. + * Provides modern async/await interface while maintaining backward compatibility. + */ +declare class DrmModule extends NativeModule { + initializeWithConfig(nativeId: string, config: DrmConfig): Promise; + destroy(nativeId: string): Promise; + setPreparedCertificate(id: string, certificate: string): any; + setPreparedMessage(id: string, message?: string): any; + setPreparedSyncMessage(id: string, syncMessage?: string): any; + setPreparedLicense(id: string, license?: string): any; + setPreparedLicenseServerUrl(id: string, url?: string): any; + setPreparedContentId(id: string, contentId?: string): any; +} +declare const _default: DrmModule; +export default _default; +//# sourceMappingURL=drmModule.d.ts.map \ No newline at end of file diff --git a/build/drm/drmModule.d.ts.map b/build/drm/drmModule.d.ts.map new file mode 100644 index 00000000..72c2072b --- /dev/null +++ b/build/drm/drmModule.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"drmModule.d.ts","sourceRoot":"","sources":["../../src/drm/drmModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,MAAM,MAAM,eAAe,GAAG;IAC5B,oBAAoB,EAAE,CAAC,EACrB,QAAQ,EACR,EAAE,EACF,WAAW,GACZ,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,EAAE,EAAE,MAAM,CAAC;QACX,WAAW,EAAE,MAAM,CAAC;KACrB,KAAK,IAAI,CAAC;IACX,gBAAgB,EAAE,CAAC,EACjB,QAAQ,EACR,EAAE,EACF,IAAI,EACJ,OAAO,EACP,OAAO,GACR,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,KAAK,IAAI,CAAC;IACX,oBAAoB,EAAE,CAAC,EACrB,QAAQ,EACR,EAAE,EACF,WAAW,EACX,OAAO,GACR,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,EAAE,EAAE,MAAM,CAAC;QACX,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;KACjB,KAAK,IAAI,CAAC;IACX,gBAAgB,EAAE,CAAC,EACjB,QAAQ,EACR,EAAE,EACF,IAAI,EACJ,OAAO,GACR,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,KAAK,IAAI,CAAC;IACX,yBAAyB,EAAE,CAAC,EAC1B,QAAQ,EACR,EAAE,EACF,gBAAgB,GACjB,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,EAAE,EAAE,MAAM,CAAC;QACX,gBAAgB,EAAE,MAAM,CAAC;KAC1B,KAAK,IAAI,CAAC;IACX,kBAAkB,EAAE,CAAC,EACnB,QAAQ,EACR,EAAE,EACF,SAAS,GACV,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,EAAE,EAAE,MAAM,CAAC;QACX,SAAS,EAAE,MAAM,CAAC;KACnB,KAAK,IAAI,CAAC;CACZ,CAAC;AAEF;;;GAGG;AACH,OAAO,OAAO,SAAU,SAAQ,YAAY,CAAC,eAAe,CAAC;IAC3D,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IACxE,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IACxC,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,GAAG;IAC5D,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG;IACrD,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,GAAG;IAC7D,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,GAAG;IACrD,2BAA2B,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,GAAG;IAC1D,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,GAAG;CAC1D;;AAED,wBAA2D"} \ No newline at end of file diff --git a/build/drm/drmModule.js b/build/drm/drmModule.js new file mode 100644 index 00000000..a9d14de0 --- /dev/null +++ b/build/drm/drmModule.js @@ -0,0 +1,3 @@ +import { requireNativeModule } from 'expo-modules-core'; +export default requireNativeModule('DrmModule'); +//# sourceMappingURL=drmModule.js.map \ No newline at end of file diff --git a/build/drm/drmModule.js.map b/build/drm/drmModule.js.map new file mode 100644 index 00000000..d723f338 --- /dev/null +++ b/build/drm/drmModule.js.map @@ -0,0 +1 @@ +{"version":3,"file":"drmModule.js","sourceRoot":"","sources":["../../src/drm/drmModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAmFtE,eAAe,mBAAmB,CAAY,WAAW,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo-modules-core';\nimport { DrmConfig } from './index';\n\nexport type DrmModuleEvents = {\n onPrepareCertificate: ({\n nativeId,\n id,\n certificate,\n }: {\n nativeId: string;\n id: string;\n certificate: string;\n }) => void;\n onPrepareMessage: ({\n nativeId,\n id,\n data,\n message,\n assetId,\n }: {\n nativeId: string;\n id: string;\n data?: string;\n message?: string;\n assetId?: string;\n }) => void;\n onPrepareSyncMessage: ({\n nativeId,\n id,\n syncMessage,\n assetId,\n }: {\n nativeId: string;\n id: string;\n syncMessage: string;\n assetId: string;\n }) => void;\n onPrepareLicense: ({\n nativeId,\n id,\n data,\n license,\n }: {\n nativeId: string;\n id: string;\n data?: string;\n license?: string;\n }) => void;\n onPrepareLicenseServerUrl: ({\n nativeId,\n id,\n licenseServerUrl,\n }: {\n nativeId: string;\n id: string;\n licenseServerUrl: string;\n }) => void;\n onPrepareContentId: ({\n nativeId,\n id,\n contentId,\n }: {\n nativeId: string;\n id: string;\n contentId: string;\n }) => void;\n};\n\n/**\n * Native DrmModule using Expo modules API.\n * Provides modern async/await interface while maintaining backward compatibility.\n */\ndeclare class DrmModule extends NativeModule {\n initializeWithConfig(nativeId: string, config: DrmConfig): Promise;\n destroy(nativeId: string): Promise;\n setPreparedCertificate(id: string, certificate: string): any;\n setPreparedMessage(id: string, message?: string): any;\n setPreparedSyncMessage(id: string, syncMessage?: string): any;\n setPreparedLicense(id: string, license?: string): any;\n setPreparedLicenseServerUrl(id: string, url?: string): any;\n setPreparedContentId(id: string, contentId?: string): any;\n}\n\nexport default requireNativeModule('DrmModule');\n"]} \ No newline at end of file diff --git a/build/drm/fairplayConfig.d.ts b/build/drm/fairplayConfig.d.ts new file mode 100644 index 00000000..11266cbe --- /dev/null +++ b/build/drm/fairplayConfig.d.ts @@ -0,0 +1,91 @@ +/** + * Represents a FairPlay Streaming DRM config. + */ +export interface FairplayConfig { + /** + * The DRM license acquisition URL. + */ + licenseUrl: string; + /** + * The URL to the FairPlay Streaming certificate of the license server. + */ + certificateUrl?: string; + /** + * A dictionary to specify custom HTTP headers for the license request. + */ + licenseRequestHeaders?: Record; + /** + * A dictionary to specify custom HTTP headers for the certificate request. + */ + certificateRequestHeaders?: Record; + /** + * A block to prepare the loaded certificate before building SPC data and passing it into the + * system. This is needed if the server responds with anything else than the certificate, e.g. if + * the certificate is wrapped into a JSON object. The server response for the certificate request + * is passed as parameter “as is”. + * + * Note that both the passed `certificate` data and this block return value should be a Base64 + * string. So use whatever solution suits you best to handle Base64 in React Native. + * + * @param certificate - Base64 encoded certificate data. + * @returns The processed Base64 encoded certificate. + */ + prepareCertificate?: (certificate: string) => string; + /** + * A block to prepare the data which is sent as the body of the POST license request. + * As many DRM providers expect different, vendor-specific messages, this can be done using + * this user-defined block. + * + * Note that both the passed `message` data and this block return value should be a Base64 string. + * So use whatever solution suits you best to handle Base64 in React Native. + * + * @param message - Base64 encoded message data. + * @param assetId - Stream asset ID. + * @returns The processed Base64 encoded message. + */ + prepareMessage?: (message: string, assetId: string) => string; + /** + * A block to prepare the data which is sent as the body of the POST request for syncing the DRM + * license information. + * + * Note that both the passed `syncMessage` data and this block return value should be a Base64 + * string. So use whatever solution suits you best to handle Base64 in React Native. + * + * @param syncMessage - Base64 encoded message data. + * @param assetId - Asset ID. + * @returns The processed Base64 encoded sync message. + */ + prepareSyncMessage?: (syncMessage: string, assetId: string) => string; + /** + * A block to prepare the loaded CKC Data before passing it to the system. This is needed if the + * server responds with anything else than the license, e.g. if the license is wrapped into a JSON + * object. + * + * Note that both the passed `license` data and this block return value should be a Base64 string. + * So use whatever solution suits you best to handle Base64 in React Native. + * + * @param license - Base64 encoded license data. + * @returns The processed Base64 encoded license. + */ + prepareLicense?: (license: string) => string; + /** + * A block to prepare the URI (without the skd://) from the HLS manifest before passing it to the + * system. + * + * @param licenseServerUrl - License server URL string. + * @returns The processed license server URL string. + */ + prepareLicenseServerUrl?: (licenseServerUrl: string) => string; + /** + * A block to prepare the `contentId`, which is sent to the FairPlay Streaming license server as + * request body, and which is used to build the SPC data. As many DRM providers expect different, + * vendor-specific messages, this can be done using this user-defined block. The parameter is the + * skd:// URI extracted from the HLS manifest (m3u8) and the return value should be the contentID + * as string. + * + * @param contentId - Extracted content id string. + * @returns The processed contentId. + */ + prepareContentId?: (contentId: string) => string; +} +//# sourceMappingURL=fairplayConfig.d.ts.map \ No newline at end of file diff --git a/build/drm/fairplayConfig.d.ts.map b/build/drm/fairplayConfig.d.ts.map new file mode 100644 index 00000000..b4e87716 --- /dev/null +++ b/build/drm/fairplayConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"fairplayConfig.d.ts","sourceRoot":"","sources":["../../src/drm/fairplayConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C;;OAEG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD;;;;;;;;;;;OAWG;IACH,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,CAAC;IACrD;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9D;;;;;;;;;;OAUG;IACH,kBAAkB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IACtE;;;;;;;;;;OAUG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7C;;;;;;OAMG;IACH,uBAAuB,CAAC,EAAE,CAAC,gBAAgB,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/D;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;CAClD"} \ No newline at end of file diff --git a/build/drm/fairplayConfig.js b/build/drm/fairplayConfig.js new file mode 100644 index 00000000..74d58950 --- /dev/null +++ b/build/drm/fairplayConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=fairplayConfig.js.map \ No newline at end of file diff --git a/build/drm/fairplayConfig.js.map b/build/drm/fairplayConfig.js.map new file mode 100644 index 00000000..b709c4f1 --- /dev/null +++ b/build/drm/fairplayConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"fairplayConfig.js","sourceRoot":"","sources":["../../src/drm/fairplayConfig.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Represents a FairPlay Streaming DRM config.\n */\nexport interface FairplayConfig {\n /**\n * The DRM license acquisition URL.\n */\n licenseUrl: string;\n /**\n * The URL to the FairPlay Streaming certificate of the license server.\n */\n certificateUrl?: string;\n /**\n * A dictionary to specify custom HTTP headers for the license request.\n */\n licenseRequestHeaders?: Record;\n /**\n * A dictionary to specify custom HTTP headers for the certificate request.\n */\n certificateRequestHeaders?: Record;\n /**\n * A block to prepare the loaded certificate before building SPC data and passing it into the\n * system. This is needed if the server responds with anything else than the certificate, e.g. if\n * the certificate is wrapped into a JSON object. The server response for the certificate request\n * is passed as parameter “as is”.\n *\n * Note that both the passed `certificate` data and this block return value should be a Base64\n * string. So use whatever solution suits you best to handle Base64 in React Native.\n *\n * @param certificate - Base64 encoded certificate data.\n * @returns The processed Base64 encoded certificate.\n */\n prepareCertificate?: (certificate: string) => string;\n /**\n * A block to prepare the data which is sent as the body of the POST license request.\n * As many DRM providers expect different, vendor-specific messages, this can be done using\n * this user-defined block.\n *\n * Note that both the passed `message` data and this block return value should be a Base64 string.\n * So use whatever solution suits you best to handle Base64 in React Native.\n *\n * @param message - Base64 encoded message data.\n * @param assetId - Stream asset ID.\n * @returns The processed Base64 encoded message.\n */\n prepareMessage?: (message: string, assetId: string) => string;\n /**\n * A block to prepare the data which is sent as the body of the POST request for syncing the DRM\n * license information.\n *\n * Note that both the passed `syncMessage` data and this block return value should be a Base64\n * string. So use whatever solution suits you best to handle Base64 in React Native.\n *\n * @param syncMessage - Base64 encoded message data.\n * @param assetId - Asset ID.\n * @returns The processed Base64 encoded sync message.\n */\n prepareSyncMessage?: (syncMessage: string, assetId: string) => string;\n /**\n * A block to prepare the loaded CKC Data before passing it to the system. This is needed if the\n * server responds with anything else than the license, e.g. if the license is wrapped into a JSON\n * object.\n *\n * Note that both the passed `license` data and this block return value should be a Base64 string.\n * So use whatever solution suits you best to handle Base64 in React Native.\n *\n * @param license - Base64 encoded license data.\n * @returns The processed Base64 encoded license.\n */\n prepareLicense?: (license: string) => string;\n /**\n * A block to prepare the URI (without the skd://) from the HLS manifest before passing it to the\n * system.\n *\n * @param licenseServerUrl - License server URL string.\n * @returns The processed license server URL string.\n */\n prepareLicenseServerUrl?: (licenseServerUrl: string) => string;\n /**\n * A block to prepare the `contentId`, which is sent to the FairPlay Streaming license server as\n * request body, and which is used to build the SPC data. As many DRM providers expect different,\n * vendor-specific messages, this can be done using this user-defined block. The parameter is the\n * skd:// URI extracted from the HLS manifest (m3u8) and the return value should be the contentID\n * as string.\n *\n * @param contentId - Extracted content id string.\n * @returns The processed contentId.\n */\n prepareContentId?: (contentId: string) => string;\n}\n"]} \ No newline at end of file diff --git a/build/drm/index.d.ts b/build/drm/index.d.ts new file mode 100644 index 00000000..71ac21c8 --- /dev/null +++ b/build/drm/index.d.ts @@ -0,0 +1,112 @@ +import NativeInstance, { NativeInstanceConfig } from '../nativeInstance'; +import { FairplayConfig } from './fairplayConfig'; +import { WidevineConfig } from './widevineConfig'; +export { FairplayConfig, WidevineConfig }; +/** + * Represents the general Streaming DRM config. + */ +export interface DrmConfig extends NativeInstanceConfig { + /** + * FairPlay specific configuration. + * + * @remarks Platform: iOS + */ + fairplay?: FairplayConfig; + /** + * Widevine specific configuration. + * + * @remarks Platform: Android, iOS (only for casting). + */ + widevine?: WidevineConfig; +} +/** + * Represents a native DRM configuration object. + * @internal + */ +export declare class Drm extends NativeInstance { + /** + * Whether this object's native instance has been created. + */ + isInitialized: boolean; + /** + * Whether this object's native instance has been disposed. + */ + isDestroyed: boolean; + private eventSubscriptions; + /** + * Allocates the DRM config instance and its resources natively. + */ + initialize: () => Promise; + /** + * Destroys the native DRM config and releases all of its allocated resources. + */ + destroy: () => Promise; + /** + * Sets up event listeners for all DRM preparation callbacks + */ + private setupEventListeners; + /** + * iOS only. + * + * Applies the user-defined `prepareCertificate` function to native's `certificate` data and store + * the result back in `DrmModule`. + * + * Called from native code when `FairplayConfig.prepareCertificate` is dispatched. + * + * @param certificate - Base64 encoded certificate data. + */ + private onPrepareCertificate; + /** + * Applies the user-defined `prepareMessage` function to native's `message` data and store + * the result back in `DrmModule`. + * + * Called from native code when `prepareMessage` is dispatched. + * + * @param message - Base64 encoded message data. + * @param assetId - Optional asset ID. Only sent by iOS. + */ + private onPrepareMessage; + /** + * iOS only. + * + * Applies the user-defined `prepareSyncMessage` function to native's `syncMessage` data and + * store the result back in `DrmModule`. + * + * Called from native code when `FairplayConfig.prepareSyncMessage` is dispatched. + * + * @param syncMessage - Base64 encoded sync SPC message data. + */ + private onPrepareSyncMessage; + /** + * Applies the user-defined `prepareLicense` function to native's `license` data and store + * the result back in `DrmModule`. + * + * Called from native code when `prepareLicense` is dispatched. + * + * @param license - Base64 encoded license data. + */ + private onPrepareLicense; + /** + * iOS only. + * + * Applies the user-defined `prepareLicenseServerUrl` function to native's `licenseServerUrl` data + * and store the result back in `DrmModule`. + * + * Called from native code when `FairplayConfig.prepareLicenseServerUrl` is dispatched. + * + * @param licenseServerUrl - The license server URL string. + */ + private onPrepareLicenseServerUrl; + /** + * iOS only. + * + * Applies the user-defined `prepareContentId` function to native's `contentId` string + * and store the result back in `DrmModule`. + * + * Called from native code when `FairplayConfig.prepareContentId` is dispatched. + * + * @param contentId - The extracted contentId string. + */ + private onPrepareContentId; +} +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/build/drm/index.d.ts.map b/build/drm/index.d.ts.map new file mode 100644 index 00000000..63bd39d1 --- /dev/null +++ b/build/drm/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/drm/index.ts"],"names":[],"mappings":"AAEA,OAAO,cAAc,EAAE,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIlD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC;AAE1C;;GAEG;AACH,MAAM,WAAW,SAAU,SAAQ,oBAAoB;IACrD;;;;OAIG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED;;;GAGG;AACH,qBAAa,GAAI,SAAQ,cAAc,CAAC,SAAS,CAAC;IAChD;;OAEG;IACH,aAAa,UAAS;IACtB;;OAEG;IACH,WAAW,UAAS;IAEpB,OAAO,CAAC,kBAAkB,CAA2B;IAErD;;OAEG;IACH,UAAU,sBAWR;IAEF;;OAEG;IACH,OAAO,sBAQL;IAEF;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAkE3B;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB,CAK1B;IAEF;;;;;;;;OAQG;IACH,OAAO,CAAC,gBAAgB,CAkBtB;IAEF;;;;;;;;;OASG;IACH,OAAO,CAAC,oBAAoB,CAY1B;IAEF;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB,CAYtB;IAEF;;;;;;;;;OASG;IACH,OAAO,CAAC,yBAAyB,CAS/B;IAEF;;;;;;;;;OASG;IACH,OAAO,CAAC,kBAAkB,CAMxB;CACH"} \ No newline at end of file diff --git a/build/drm/index.js b/build/drm/index.js new file mode 100644 index 00000000..2307b3c0 --- /dev/null +++ b/build/drm/index.js @@ -0,0 +1,191 @@ +import { Platform } from 'react-native'; +import NativeInstance from '../nativeInstance'; +import DrmModule from './drmModule'; +/** + * Represents a native DRM configuration object. + * @internal + */ +export class Drm extends NativeInstance { + /** + * Whether this object's native instance has been created. + */ + isInitialized = false; + /** + * Whether this object's native instance has been disposed. + */ + isDestroyed = false; + eventSubscriptions = []; + /** + * Allocates the DRM config instance and its resources natively. + */ + initialize = async () => { + if (!this.isInitialized) { + // Set up event listeners for DRM preparation callbacks + this.setupEventListeners(); + // Create native configuration object using Expo module. + if (this.config) { + await DrmModule.initializeWithConfig(this.nativeId, this.config); + } + this.isInitialized = true; + } + }; + /** + * Destroys the native DRM config and releases all of its allocated resources. + */ + destroy = async () => { + if (!this.isDestroyed) { + await DrmModule.destroy(this.nativeId); + // Clean up event subscriptions + this.eventSubscriptions.forEach((subscription) => subscription.remove()); + this.eventSubscriptions = []; + this.isDestroyed = true; + } + }; + /** + * Sets up event listeners for all DRM preparation callbacks + */ + setupEventListeners() { + // iOS-only events + this.eventSubscriptions.push(DrmModule.addListener('onPrepareCertificate', ({ nativeId, id, certificate }) => { + if (nativeId !== this.nativeId) + return; + this.onPrepareCertificate(id, certificate); + })); + this.eventSubscriptions.push(DrmModule.addListener('onPrepareSyncMessage', ({ nativeId, id, syncMessage, assetId }) => { + if (nativeId !== this.nativeId) + return; + this.onPrepareSyncMessage(id, syncMessage, assetId); + })); + this.eventSubscriptions.push(DrmModule.addListener('onPrepareLicenseServerUrl', ({ nativeId, id, licenseServerUrl }) => { + if (nativeId !== this.nativeId) + return; + this.onPrepareLicenseServerUrl(id, licenseServerUrl); + })); + this.eventSubscriptions.push(DrmModule.addListener('onPrepareContentId', ({ nativeId, id, contentId }) => { + if (nativeId !== this.nativeId) + return; + this.onPrepareContentId(id, contentId); + })); + // Cross-platform events + this.eventSubscriptions.push(DrmModule.addListener('onPrepareMessage', ({ nativeId, id, data, message, assetId }) => { + if (nativeId !== this.nativeId) + return; + // Android sends 'data', iOS sends 'message' + this.onPrepareMessage(id, data || message, assetId); + })); + this.eventSubscriptions.push(DrmModule.addListener('onPrepareLicense', ({ nativeId, id, data, license }) => { + if (nativeId !== this.nativeId) + return; + // Android sends 'data', iOS sends 'license' + this.onPrepareLicense(id, data || license); + })); + } + /** + * iOS only. + * + * Applies the user-defined `prepareCertificate` function to native's `certificate` data and store + * the result back in `DrmModule`. + * + * Called from native code when `FairplayConfig.prepareCertificate` is dispatched. + * + * @param certificate - Base64 encoded certificate data. + */ + onPrepareCertificate = (id, certificate) => { + if (this.config?.fairplay?.prepareCertificate) { + const result = this.config?.fairplay?.prepareCertificate?.(certificate); + DrmModule.setPreparedCertificate(id, result); + } + }; + /** + * Applies the user-defined `prepareMessage` function to native's `message` data and store + * the result back in `DrmModule`. + * + * Called from native code when `prepareMessage` is dispatched. + * + * @param message - Base64 encoded message data. + * @param assetId - Optional asset ID. Only sent by iOS. + */ + onPrepareMessage = (id, message, assetId) => { + if (!message) { + DrmModule.setPreparedMessage(id, undefined); + return; + } + const config = Platform.OS === 'ios' ? this.config?.fairplay : this.config?.widevine; + if (config && config.prepareMessage) { + const result = Platform.OS === 'ios' + ? config.prepareMessage?.(message, assetId) + : config.prepareMessage?.(message); + DrmModule.setPreparedMessage(id, result); + } + }; + /** + * iOS only. + * + * Applies the user-defined `prepareSyncMessage` function to native's `syncMessage` data and + * store the result back in `DrmModule`. + * + * Called from native code when `FairplayConfig.prepareSyncMessage` is dispatched. + * + * @param syncMessage - Base64 encoded sync SPC message data. + */ + onPrepareSyncMessage = (id, syncMessage, assetId) => { + if (this.config?.fairplay?.prepareSyncMessage) { + const result = this.config?.fairplay?.prepareSyncMessage?.(syncMessage, assetId); + DrmModule.setPreparedSyncMessage(id, result); + } + }; + /** + * Applies the user-defined `prepareLicense` function to native's `license` data and store + * the result back in `DrmModule`. + * + * Called from native code when `prepareLicense` is dispatched. + * + * @param license - Base64 encoded license data. + */ + onPrepareLicense = (id, license) => { + if (!license) { + DrmModule.setPreparedLicense(id, undefined); + return; + } + const prepareLicense = Platform.OS === 'ios' + ? this.config?.fairplay?.prepareLicense + : this.config?.widevine?.prepareLicense; + if (prepareLicense) { + DrmModule.setPreparedLicense(id, prepareLicense(license)); + } + }; + /** + * iOS only. + * + * Applies the user-defined `prepareLicenseServerUrl` function to native's `licenseServerUrl` data + * and store the result back in `DrmModule`. + * + * Called from native code when `FairplayConfig.prepareLicenseServerUrl` is dispatched. + * + * @param licenseServerUrl - The license server URL string. + */ + onPrepareLicenseServerUrl = (id, licenseServerUrl) => { + if (this.config?.fairplay?.prepareLicenseServerUrl) { + const result = this.config?.fairplay?.prepareLicenseServerUrl?.(licenseServerUrl); + DrmModule.setPreparedLicenseServerUrl(id, result); + } + }; + /** + * iOS only. + * + * Applies the user-defined `prepareContentId` function to native's `contentId` string + * and store the result back in `DrmModule`. + * + * Called from native code when `FairplayConfig.prepareContentId` is dispatched. + * + * @param contentId - The extracted contentId string. + */ + onPrepareContentId = (id, contentId) => { + console.log('onPrepareContentId', contentId); + if (this.config?.fairplay?.prepareContentId) { + const result = this.config?.fairplay?.prepareContentId?.(contentId); + DrmModule.setPreparedContentId(id, result); + } + }; +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/build/drm/index.js.map b/build/drm/index.js.map new file mode 100644 index 00000000..2ebd4dd2 --- /dev/null +++ b/build/drm/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/drm/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from 'react-native';\nimport { EventSubscription } from 'expo-modules-core';\nimport NativeInstance, { NativeInstanceConfig } from '../nativeInstance';\nimport { FairplayConfig } from './fairplayConfig';\nimport { WidevineConfig } from './widevineConfig';\nimport DrmModule from './drmModule';\n\n// Export config types from DRM module.\nexport { FairplayConfig, WidevineConfig };\n\n/**\n * Represents the general Streaming DRM config.\n */\nexport interface DrmConfig extends NativeInstanceConfig {\n /**\n * FairPlay specific configuration.\n *\n * @remarks Platform: iOS\n */\n fairplay?: FairplayConfig;\n /**\n * Widevine specific configuration.\n *\n * @remarks Platform: Android, iOS (only for casting).\n */\n widevine?: WidevineConfig;\n}\n\n/**\n * Represents a native DRM configuration object.\n * @internal\n */\nexport class Drm extends NativeInstance {\n /**\n * Whether this object's native instance has been created.\n */\n isInitialized = false;\n /**\n * Whether this object's native instance has been disposed.\n */\n isDestroyed = false;\n\n private eventSubscriptions: EventSubscription[] = [];\n\n /**\n * Allocates the DRM config instance and its resources natively.\n */\n initialize = async () => {\n if (!this.isInitialized) {\n // Set up event listeners for DRM preparation callbacks\n this.setupEventListeners();\n\n // Create native configuration object using Expo module.\n if (this.config) {\n await DrmModule.initializeWithConfig(this.nativeId, this.config);\n }\n this.isInitialized = true;\n }\n };\n\n /**\n * Destroys the native DRM config and releases all of its allocated resources.\n */\n destroy = async () => {\n if (!this.isDestroyed) {\n await DrmModule.destroy(this.nativeId);\n // Clean up event subscriptions\n this.eventSubscriptions.forEach((subscription) => subscription.remove());\n this.eventSubscriptions = [];\n this.isDestroyed = true;\n }\n };\n\n /**\n * Sets up event listeners for all DRM preparation callbacks\n */\n private setupEventListeners() {\n // iOS-only events\n this.eventSubscriptions.push(\n DrmModule.addListener(\n 'onPrepareCertificate',\n ({ nativeId, id, certificate }) => {\n if (nativeId !== this.nativeId) return;\n this.onPrepareCertificate(id, certificate);\n }\n )\n );\n\n this.eventSubscriptions.push(\n DrmModule.addListener(\n 'onPrepareSyncMessage',\n ({ nativeId, id, syncMessage, assetId }) => {\n if (nativeId !== this.nativeId) return;\n this.onPrepareSyncMessage(id, syncMessage, assetId);\n }\n )\n );\n\n this.eventSubscriptions.push(\n DrmModule.addListener(\n 'onPrepareLicenseServerUrl',\n ({ nativeId, id, licenseServerUrl }) => {\n if (nativeId !== this.nativeId) return;\n this.onPrepareLicenseServerUrl(id, licenseServerUrl);\n }\n )\n );\n\n this.eventSubscriptions.push(\n DrmModule.addListener(\n 'onPrepareContentId',\n ({ nativeId, id, contentId }) => {\n if (nativeId !== this.nativeId) return;\n this.onPrepareContentId(id, contentId);\n }\n )\n );\n\n // Cross-platform events\n this.eventSubscriptions.push(\n DrmModule.addListener(\n 'onPrepareMessage',\n ({ nativeId, id, data, message, assetId }) => {\n if (nativeId !== this.nativeId) return;\n // Android sends 'data', iOS sends 'message'\n this.onPrepareMessage(id, data || message, assetId);\n }\n )\n );\n\n this.eventSubscriptions.push(\n DrmModule.addListener(\n 'onPrepareLicense',\n ({ nativeId, id, data, license }) => {\n if (nativeId !== this.nativeId) return;\n // Android sends 'data', iOS sends 'license'\n this.onPrepareLicense(id, data || license);\n }\n )\n );\n }\n\n /**\n * iOS only.\n *\n * Applies the user-defined `prepareCertificate` function to native's `certificate` data and store\n * the result back in `DrmModule`.\n *\n * Called from native code when `FairplayConfig.prepareCertificate` is dispatched.\n *\n * @param certificate - Base64 encoded certificate data.\n */\n private onPrepareCertificate = (id: string, certificate: string) => {\n if (this.config?.fairplay?.prepareCertificate) {\n const result = this.config?.fairplay?.prepareCertificate?.(certificate);\n DrmModule.setPreparedCertificate(id, result);\n }\n };\n\n /**\n * Applies the user-defined `prepareMessage` function to native's `message` data and store\n * the result back in `DrmModule`.\n *\n * Called from native code when `prepareMessage` is dispatched.\n *\n * @param message - Base64 encoded message data.\n * @param assetId - Optional asset ID. Only sent by iOS.\n */\n private onPrepareMessage = (\n id: string,\n message?: string,\n assetId?: string\n ) => {\n if (!message) {\n DrmModule.setPreparedMessage(id, undefined);\n return;\n }\n const config =\n Platform.OS === 'ios' ? this.config?.fairplay : this.config?.widevine;\n if (config && config.prepareMessage) {\n const result =\n Platform.OS === 'ios'\n ? (config as FairplayConfig).prepareMessage?.(message, assetId!)\n : (config as WidevineConfig).prepareMessage?.(message);\n DrmModule.setPreparedMessage(id, result);\n }\n };\n\n /**\n * iOS only.\n *\n * Applies the user-defined `prepareSyncMessage` function to native's `syncMessage` data and\n * store the result back in `DrmModule`.\n *\n * Called from native code when `FairplayConfig.prepareSyncMessage` is dispatched.\n *\n * @param syncMessage - Base64 encoded sync SPC message data.\n */\n private onPrepareSyncMessage = (\n id: string,\n syncMessage: string,\n assetId: string\n ) => {\n if (this.config?.fairplay?.prepareSyncMessage) {\n const result = this.config?.fairplay?.prepareSyncMessage?.(\n syncMessage,\n assetId\n );\n DrmModule.setPreparedSyncMessage(id, result);\n }\n };\n\n /**\n * Applies the user-defined `prepareLicense` function to native's `license` data and store\n * the result back in `DrmModule`.\n *\n * Called from native code when `prepareLicense` is dispatched.\n *\n * @param license - Base64 encoded license data.\n */\n private onPrepareLicense = (id: string, license?: string) => {\n if (!license) {\n DrmModule.setPreparedLicense(id, undefined);\n return;\n }\n const prepareLicense =\n Platform.OS === 'ios'\n ? this.config?.fairplay?.prepareLicense\n : this.config?.widevine?.prepareLicense;\n if (prepareLicense) {\n DrmModule.setPreparedLicense(id, prepareLicense(license));\n }\n };\n\n /**\n * iOS only.\n *\n * Applies the user-defined `prepareLicenseServerUrl` function to native's `licenseServerUrl` data\n * and store the result back in `DrmModule`.\n *\n * Called from native code when `FairplayConfig.prepareLicenseServerUrl` is dispatched.\n *\n * @param licenseServerUrl - The license server URL string.\n */\n private onPrepareLicenseServerUrl = (\n id: string,\n licenseServerUrl: string\n ) => {\n if (this.config?.fairplay?.prepareLicenseServerUrl) {\n const result =\n this.config?.fairplay?.prepareLicenseServerUrl?.(licenseServerUrl);\n DrmModule.setPreparedLicenseServerUrl(id, result);\n }\n };\n\n /**\n * iOS only.\n *\n * Applies the user-defined `prepareContentId` function to native's `contentId` string\n * and store the result back in `DrmModule`.\n *\n * Called from native code when `FairplayConfig.prepareContentId` is dispatched.\n *\n * @param contentId - The extracted contentId string.\n */\n private onPrepareContentId = (id: string, contentId: string) => {\n console.log('onPrepareContentId', contentId);\n if (this.config?.fairplay?.prepareContentId) {\n const result = this.config?.fairplay?.prepareContentId?.(contentId);\n DrmModule.setPreparedContentId(id, result);\n }\n };\n}\n"]} \ No newline at end of file diff --git a/build/drm/widevineConfig.d.ts b/build/drm/widevineConfig.d.ts new file mode 100644 index 00000000..dc4a7147 --- /dev/null +++ b/build/drm/widevineConfig.d.ts @@ -0,0 +1,58 @@ +/** + * Represents a Widevine Streaming DRM config. + * @remarks Platform: Android, iOS (only for casting). + */ +export interface WidevineConfig { + /** + * The DRM license acquisition URL. + */ + licenseUrl: string; + /** + * A map containing the HTTP request headers, or null. + */ + httpHeaders?: Record; + /** + * A block to prepare the data which is sent as the body of the POST license request. + * As many DRM providers expect different, vendor-specific messages, this can be done using + * this user-defined block. + * + * Note that both the passed `message` data and this block return value should be a Base64 string. + * So use whatever solution suits you best to handle Base64 in React Native. + * + * @remarks Platform: Android + * + * @param message - Base64 encoded message data. + * @returns The processed Base64 encoded message. + */ + prepareMessage?: (message: string) => string; + /** + * A block to prepare the loaded CKC Data before passing it to the system. This is needed if the + * server responds with anything else than the license, e.g. if the license is wrapped into a JSON + * object. + * + * Note that both the passed `license` data and this block return value should be a Base64 string. + * So use whatever solution suits you best to handle Base64 in React Native. + * + * @remarks Platform: Android + * + * @param license - Base64 encoded license data. + * @returns The processed Base64 encoded license. + */ + prepareLicense?: (license: string) => string; + /** + * Set widevine's preferred security level. + * + * @remarks Platform: Android + */ + preferredSecurityLevel?: string; + /** + * Indicates if the DRM sessions should be kept alive after a source is unloaded. + * This allows DRM sessions to be reused over several different source items with the same DRM configuration as well + * as the same DRM scheme information. + * Default: `false` + * + * @remarks Platform: Android + */ + shouldKeepDrmSessionsAlive?: boolean; +} +//# sourceMappingURL=widevineConfig.d.ts.map \ No newline at end of file diff --git a/build/drm/widevineConfig.d.ts.map b/build/drm/widevineConfig.d.ts.map new file mode 100644 index 00000000..76558f69 --- /dev/null +++ b/build/drm/widevineConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"widevineConfig.d.ts","sourceRoot":"","sources":["../../src/drm/widevineConfig.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC;;;;;;;;;;;;OAYG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7C;;;;;;;;;;;;OAYG;IACH,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7C;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;;OAOG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;CACtC"} \ No newline at end of file diff --git a/build/drm/widevineConfig.js b/build/drm/widevineConfig.js new file mode 100644 index 00000000..2737c579 --- /dev/null +++ b/build/drm/widevineConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=widevineConfig.js.map \ No newline at end of file diff --git a/build/drm/widevineConfig.js.map b/build/drm/widevineConfig.js.map new file mode 100644 index 00000000..cebdca81 --- /dev/null +++ b/build/drm/widevineConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"widevineConfig.js","sourceRoot":"","sources":["../../src/drm/widevineConfig.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Represents a Widevine Streaming DRM config.\n * @remarks Platform: Android, iOS (only for casting).\n */\nexport interface WidevineConfig {\n /**\n * The DRM license acquisition URL.\n */\n licenseUrl: string;\n /**\n * A map containing the HTTP request headers, or null.\n */\n httpHeaders?: Record;\n /**\n * A block to prepare the data which is sent as the body of the POST license request.\n * As many DRM providers expect different, vendor-specific messages, this can be done using\n * this user-defined block.\n *\n * Note that both the passed `message` data and this block return value should be a Base64 string.\n * So use whatever solution suits you best to handle Base64 in React Native.\n *\n * @remarks Platform: Android\n *\n * @param message - Base64 encoded message data.\n * @returns The processed Base64 encoded message.\n */\n prepareMessage?: (message: string) => string;\n /**\n * A block to prepare the loaded CKC Data before passing it to the system. This is needed if the\n * server responds with anything else than the license, e.g. if the license is wrapped into a JSON\n * object.\n *\n * Note that both the passed `license` data and this block return value should be a Base64 string.\n * So use whatever solution suits you best to handle Base64 in React Native.\n *\n * @remarks Platform: Android\n *\n * @param license - Base64 encoded license data.\n * @returns The processed Base64 encoded license.\n */\n prepareLicense?: (license: string) => string;\n /**\n * Set widevine's preferred security level.\n *\n * @remarks Platform: Android\n */\n preferredSecurityLevel?: string;\n /**\n * Indicates if the DRM sessions should be kept alive after a source is unloaded.\n * This allows DRM sessions to be reused over several different source items with the same DRM configuration as well\n * as the same DRM scheme information.\n * Default: `false`\n *\n * @remarks Platform: Android\n */\n shouldKeepDrmSessionsAlive?: boolean;\n}\n"]} \ No newline at end of file diff --git a/build/events.d.ts b/build/events.d.ts new file mode 100644 index 00000000..afc6a85a --- /dev/null +++ b/build/events.d.ts @@ -0,0 +1,692 @@ +import { Ad, AdBreak, AdConfig, AdItem, AdQuartile, AdSourceType } from './advertising'; +import { SubtitleTrack } from './subtitleTrack'; +import { VideoQuality } from './media'; +import { AudioTrack } from './audioTrack'; +import { LoadingState } from './source'; +import { HttpRequestType } from './network/networkConfig'; +/** + * Base event type for all events. + */ +export interface Event { + /** + * This event name as it is on the native side. + */ + name: string; + /** + * The UNIX timestamp in which this event happened. + */ + timestamp: number; +} +/** + * Base event type for error and warning events. + */ +export interface ErrorEvent extends Event { + /** + * Error/Warning's code number. + */ + code?: number; + /** + * Error/Warning's localized message. + */ + message: string; + /** + * Underlying data emitted with the error or warning. + */ + data?: Record; +} +/** + * Emitted when a source is loaded into the player. + * Seeking and time shifting are allowed as soon as this event is seen. + */ +export type PlayerActiveEvent = Event; +/** + * Emitted when a source is unloaded from the player. + * Seeking and time shifting are not allowed anymore after this event. + */ +export type PlayerInactiveEvent = Event; +/** + * Emitted when a player error occurred. + */ +export type PlayerErrorEvent = ErrorEvent; +/** + * Emitted when a player warning occurred. + */ +export type PlayerWarningEvent = ErrorEvent; +/** + * Emitted when the player is destroyed. + */ +export type DestroyEvent = Event; +/** + * Emitted when the player is muted. + */ +export type MutedEvent = Event; +/** + * Emitted when the player is unmuted. + */ +export type UnmutedEvent = Event; +/** + * Emitted when the player is ready for immediate playback, because initial audio/video + * has been downloaded. + */ +export type ReadyEvent = Event; +/** + * Emitted when the player is paused. + */ +export interface PausedEvent extends Event { + /** + * The player's playback time from when this event happened. + */ + time: number; +} +/** + * Emitted when the player received an intention to start/resume playback. + */ +export interface PlayEvent extends Event { + /** + * The player's playback time from when this event happened. + */ + time: number; +} +/** + * Emitted when playback has started. + */ +export interface PlayingEvent extends Event { + /** + * The player's playback time from when this event happened. + */ + time: number; +} +/** + * Emitted when the playback of the current media has finished. + */ +export type PlaybackFinishedEvent = Event; +/** + * Source object representation the way it appears on event's payloads such as `SeekEvent`, for example. + * + * This interface only type hints what should be the shape of a {@link Source} object inside an event's + * payload during runtime so it has no direct relation with the `Source` class present in `src/source.ts`. + * + * Do not mistake it for a `NativeInstance` type. + */ +export interface EventSource { + /** + * Event's source duration in seconds. + */ + duration: number; + /** + * Whether this event's source is currently active in a player. + */ + isActive: boolean; + /** + * Whether this event's source is currently attached to a player instance. + */ + isAttachedToPlayer: boolean; + /** + * Metadata for this event's source. + */ + metadata?: Record; + /** + * The current {@link LoadingState} of the source. + */ + loadingState: LoadingState; +} +/** + * Represents a seeking position. + */ +export interface SeekPosition { + /** + * The relevant {@link Source}. + */ + source: EventSource; + /** + * The position within the {@link Source} in seconds. + */ + time: number; +} +/** + * Emitted when the player is about to seek to a new position. + * This event only applies to VoD streams. + * When looking for an equivalent for live streams, the {@link TimeShiftEvent} is relevant. + */ +export interface SeekEvent extends Event { + /** + * Origin source metadata. + */ + from: SeekPosition; + /** + * Target source metadata. + */ + to: SeekPosition; +} +/** + * Emitted when seeking has finished and data to continue playback is available. + * This event only applies to VoD streams. + * When looking for an equivalent for live streams, the {@link TimeShiftedEvent} is relevant. + */ +export type SeekedEvent = Event; +/** + * Emitted when the player starts time shifting. + * This event only applies to live streams. + * When looking for an equivalent for VoD streams, the {@link SeekEvent} is relevant. + */ +export interface TimeShiftEvent extends Event { + /** + * The position from which we start the time shift + */ + position: number; + /** + * The position to which we want to jump for the time shift + */ + targetPosition: number; +} +/** + * Emitted when time shifting has finished and data is available to continue playback. + * This event only applies to live streams. + * When looking for an equivalent for VoD streams, the {@link SeekedEvent} is relevant. + */ +export type TimeShiftedEvent = Event; +/** + * Emitted when the player begins to stall and to buffer due to an empty buffer. + */ +export type StallStartedEvent = Event; +/** + * Emitted when the player ends stalling, due to enough data in the buffer. + */ +export type StallEndedEvent = Event; +/** + * Emitted when the current playback time has changed. + */ +export interface TimeChangedEvent extends Event { + /** + * The player's playback time from when this event happened. + */ + currentTime: number; +} +/** + * Emitted when a new source loading has started. + */ +export interface SourceLoadEvent extends Event { + /** + * Source that is about to load. + */ + source: EventSource; +} +/** + * Emitted when a new source is loaded. + * This does not mean that the source is immediately ready for playback. + * {@link ReadyEvent} indicates the player is ready for immediate playback. + */ +export interface SourceLoadedEvent extends Event { + /** + * Source that was loaded into player. + */ + source: EventSource; +} +/** + * Emitted when the current source has been unloaded. + */ +export interface SourceUnloadedEvent extends Event { + /** + * Source that was unloaded from player. + */ + source: EventSource; +} +/** + * Emitted when a source error occurred. + */ +export type SourceErrorEvent = ErrorEvent; +/** + * Emitted when a source warning occurred. + */ +export type SourceWarningEvent = ErrorEvent; +/** + * Emitted when a new audio track is added to the player. + */ +export interface AudioAddedEvent extends Event { + /** + * Audio track that has been added. + */ + audioTrack: AudioTrack; +} +/** + * Emitted when the player's selected audio track has changed. + */ +export interface AudioChangedEvent extends Event { + /** + * Audio track that was previously selected. + */ + oldAudioTrack: AudioTrack; + /** + * Audio track that is selected now. + */ + newAudioTrack: AudioTrack; +} +/** + * Emitted when an audio track is removed from the player. + */ +export interface AudioRemovedEvent extends Event { + /** + * Audio track that has been removed. + */ + audioTrack: AudioTrack; +} +/** + * Emitted when a new subtitle track is added to the player. + */ +export interface SubtitleAddedEvent extends Event { + /** + * Subtitle track that has been added. + */ + subtitleTrack: SubtitleTrack; +} +/** + * Emitted when a subtitle track is removed from the player. + */ +export interface SubtitleRemovedEvent extends Event { + /** + * Subtitle track that has been removed. + */ + subtitleTrack: SubtitleTrack; +} +/** + * Emitted when the player's selected subtitle track has changed. + */ +export interface SubtitleChangedEvent extends Event { + /** + * Subtitle track that was previously selected. + */ + oldSubtitleTrack: SubtitleTrack; + /** + * Subtitle track that is selected now. + */ + newSubtitleTrack: SubtitleTrack; +} +/** + * Emitted when the player enters Picture in Picture mode. + * + * @remarks Platform: iOS, Android + */ +export type PictureInPictureEnterEvent = Event; +/** + * Emitted when the player exits Picture in Picture mode. + * + * @remarks Platform: iOS, Android + */ +export type PictureInPictureExitEvent = Event; +/** + * Emitted when the player has finished entering Picture in Picture mode on iOS. + * + * @remarks Platform: iOS + */ +export type PictureInPictureEnteredEvent = Event; +/** + * Emitted when the player has finished exiting Picture in Picture mode on iOS. + * + * @remarks Platform: iOS + */ +export type PictureInPictureExitedEvent = Event; +/** + * Emitted when the fullscreen functionality has been enabled. + * + * @remarks Platform: iOS, Android + */ +export type FullscreenEnabledEvent = Event; +/** + * Emitted when the fullscreen functionality has been disabled. + * + * @remarks Platform: iOS, Android + */ +export type FullscreenDisabledEvent = Event; +/** + * Emitted when the player enters fullscreen mode. + * + * @remarks Platform: iOS, Android + */ +export type FullscreenEnterEvent = Event; +/** + * Emitted when the player exits fullscreen mode. + * + * @remarks Platform: iOS, Android + */ +export type FullscreenExitEvent = Event; +/** + * Emitted when the availability of the Picture in Picture mode changed on Android. + * + * @remarks Platform: Android + */ +export interface PictureInPictureAvailabilityChangedEvent extends Event { + /** + * Whether Picture in Picture is available. + */ + isPictureInPictureAvailable: boolean; +} +/** + * Emitted when an ad break has started. + */ +export interface AdBreakStartedEvent extends Event { + /** + * The {@link AdBreak} that has started. + */ + adBreak?: AdBreak; +} +/** + * Emitted when an ad break has finished. + */ +export interface AdBreakFinishedEvent extends Event { + /** + * The {@link AdBreak} that has finished. + */ + adBreak?: AdBreak; +} +/** + * Emitted when the playback of an ad has started. + */ +export interface AdStartedEvent extends Event { + /** + * The {@link Ad} this event is related to. + */ + ad?: Ad; + /** + * The target URL to open once the user clicks on the ad. + */ + clickThroughUrl?: string; + /** + * The {@link AdSourceType} of the started ad. + */ + clientType?: AdSourceType; + /** + * The duration of the ad in seconds. + */ + duration: number; + /** + * The index of the ad in the queue. + */ + indexInQueue: number; + /** + * The position of the corresponding ad. + */ + position?: string; + /** + * The skip offset of the ad in seconds. + */ + skipOffset: number; + /** + * The main content time at which the ad is played. + */ + timeOffset: number; +} +/** + * Emitted when an ad has finished playback. + */ +export interface AdFinishedEvent extends Event { + /** + * The {@link Ad} that finished playback. + */ + ad?: Ad; +} +/** + * Emitted when an error with the ad playback occurs. + */ +export interface AdErrorEvent extends ErrorEvent { + /** + * The {@link AdConfig} for which the ad error occurred. + */ + adConfig?: AdConfig; + /** + * The {@link AdItem} for which the ad error occurred. + */ + adItem?: AdItem; +} +/** + * Emitted when an ad was clicked. + */ +export interface AdClickedEvent extends Event { + /** + * The click through url of the ad. + */ + clickThroughUrl?: string; +} +/** + * Emitted when an ad was skipped. + */ +export interface AdSkippedEvent extends Event { + /** + * The ad that was skipped. + */ + ad?: Ad; +} +/** + * Emitted when the playback of an ad has progressed over a quartile boundary. + */ +export interface AdQuartileEvent extends Event { + /** + * The {@link AdQuartile} boundary that playback has progressed over. + */ + quartile: AdQuartile; +} +/** + * Emitted when an ad manifest was successfully downloaded, parsed and added into the ad break schedule. + */ +export interface AdScheduledEvent extends Event { + /** + * The total number of scheduled ads. + */ + numberOfAds: number; +} +/** + * Emitted when the download of an ad manifest is started. + */ +export interface AdManifestLoadEvent extends Event { + /** + * The {@link AdBreak} this event is related to. + */ + adBreak?: AdBreak; + /** + * The {@link AdConfig} of the loaded ad manifest. + */ + adConfig?: AdConfig; +} +/** + * Emitted when an ad manifest was successfully loaded. + */ +export interface AdManifestLoadedEvent extends Event { + /** + * The {@link AdBreak} this event is related to. + */ + adBreak?: AdBreak; + /** + * The {@link AdConfig} of the loaded ad manifest. + */ + adConfig?: AdConfig; + /** + * How long it took for the ad tag to be downloaded in milliseconds. + */ + downloadTime: number; +} +/** + * Emitted when current video download quality has changed. + */ +export interface VideoDownloadQualityChangedEvent extends Event { + /** + * The new quality + */ + newVideoQuality: VideoQuality; + /** + * The previous quality + */ + oldVideoQuality: VideoQuality; +} +/** + * Emitted when the current video playback quality has changed. + */ +export interface VideoPlaybackQualityChangedEvent extends Event { + /** + * The new quality + */ + newVideoQuality: VideoQuality; + /** + * The previous quality + */ + oldVideoQuality: VideoQuality; +} +/** + * Emitted when casting to a cast-compatible device is available. + */ +export type CastAvailableEvent = Event; +/** + * Emitted when the playback on a cast-compatible device was paused. + * + * On Android {@link PausedEvent} is also emitted while casting. + */ +export type CastPausedEvent = Event; +/** + * Emitted when the playback on a cast-compatible device has finished. + * + * On Android {@link PlaybackFinishedEvent} is also emitted while casting. + */ +export type CastPlaybackFinishedEvent = Event; +/** + * Emitted when playback on a cast-compatible device has started. + * + * On Android {@link PlayingEvent} is also emitted while casting. + */ +export type CastPlayingEvent = Event; +/** + * Emitted when the cast app is launched successfully. + */ +export interface CastStartedEvent extends Event { + /** + * The name of the cast device on which the app was launched. + */ + deviceName: string | null; +} +/** + * Emitted when casting is initiated, but the user still needs to choose which device should be used. + */ +export type CastStartEvent = Event; +/** + * Emitted when casting to a cast-compatible device is stopped. + */ +export type CastStoppedEvent = Event; +/** + * Emitted when the time update from the currently used cast-compatible device is received. + */ +export type CastTimeUpdatedEvent = Event; +/** + * Contains information for the {@link CastWaitingForDeviceEvent}. + */ +export interface CastPayload { + /** + * The current time in seconds. + */ + currentTime: number; + /** + * The name of the chosen cast device. + */ + deviceName: string | null; + /** + * The type of the payload (always `"cast"`). + */ + type: string; +} +/** + * Emitted when a cast-compatible device has been chosen and the player is waiting for the device to get ready for + * playback. + */ +export interface CastWaitingForDeviceEvent extends Event { + /** + * The {@link CastPayload} object for the event + */ + castPayload: CastPayload; +} +/** + * Emitted when a download was finished. + */ +export interface DownloadFinishedEvent extends Event { + /** + * The time needed to finish the request, in seconds. + */ + downloadTime: number; + /** + * Which type of request this was. + */ + requestType: HttpRequestType; + /** + * The HTTP status code of the request. + * If opening the connection failed, a value of `0` is returned. + */ + httpStatus: number; + /** + * If the download was successful. + */ + isSuccess: boolean; + /** + * The last redirect location, or `null` if no redirect happened. + */ + lastRedirectLocation?: string; + /** + * The size of the downloaded data, in bytes. + */ + size: number; + /** + * The URL of the request. + */ + url: string; +} +/** + * Emitted when the player transitions from one playback speed to another. + * @remarks Platform: iOS, tvOS + */ +export interface PlaybackSpeedChangedEvent extends Event { + /** + * The playback speed before the change happened. + */ + from: number; + /** + * The playback speed after the change happened. + */ + to: number; +} +/** + * Emitted when a subtitle entry transitions into the active status. + */ +export interface CueEnterEvent extends Event { + /** + * The playback time in seconds when the subtitle should be rendered. + */ + start: number; + /** + * The playback time in seconds when the subtitle should be hidden. + */ + end: number; + /** + * The textual content of this subtitle. + */ + text?: string; + /** + * Data URI for image data of this subtitle. + */ + image?: string; +} +/** + * Emitted when an active subtitle entry transitions into the inactive status. + */ +export interface CueExitEvent extends Event { + /** + * The playback time in seconds when the subtitle should be rendered. + */ + start: number; + /** + * The playback time in seconds when the subtitle should be hidden. + */ + end: number; + /** + * The textual content of this subtitle. + */ + text?: string; + /** + * Data URI for image data of this subtitle. + */ + image?: string; +} +//# sourceMappingURL=events.d.ts.map \ No newline at end of file diff --git a/build/events.d.ts.map b/build/events.d.ts.map new file mode 100644 index 00000000..464df51f --- /dev/null +++ b/build/events.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/build/events.js b/build/events.js new file mode 100644 index 00000000..4b09bff3 --- /dev/null +++ b/build/events.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=events.js.map \ No newline at end of file diff --git a/build/events.js.map b/build/events.js.map new file mode 100644 index 00000000..9f5a6bb6 --- /dev/null +++ b/build/events.js.map @@ -0,0 +1 @@ +{"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"","sourcesContent":["import {\n Ad,\n AdBreak,\n AdConfig,\n AdItem,\n AdQuartile,\n AdSourceType,\n} from './advertising';\nimport { SubtitleTrack } from './subtitleTrack';\nimport { VideoQuality } from './media';\nimport { AudioTrack } from './audioTrack';\nimport { LoadingState } from './source';\nimport { HttpRequestType } from './network/networkConfig';\n\n/**\n * Base event type for all events.\n */\nexport interface Event {\n /**\n * This event name as it is on the native side.\n */\n name: string;\n /**\n * The UNIX timestamp in which this event happened.\n */\n timestamp: number;\n}\n\n/**\n * Base event type for error and warning events.\n */\nexport interface ErrorEvent extends Event {\n /**\n * Error/Warning's code number.\n */\n code?: number;\n /**\n * Error/Warning's localized message.\n */\n message: string;\n /**\n * Underlying data emitted with the error or warning.\n */\n data?: Record;\n}\n\n/**\n * Emitted when a source is loaded into the player.\n * Seeking and time shifting are allowed as soon as this event is seen.\n */\nexport type PlayerActiveEvent = Event;\n\n/**\n * Emitted when a source is unloaded from the player.\n * Seeking and time shifting are not allowed anymore after this event.\n */\nexport type PlayerInactiveEvent = Event;\n\n/**\n * Emitted when a player error occurred.\n */\nexport type PlayerErrorEvent = ErrorEvent;\n\n/**\n * Emitted when a player warning occurred.\n */\nexport type PlayerWarningEvent = ErrorEvent;\n\n/**\n * Emitted when the player is destroyed.\n */\nexport type DestroyEvent = Event;\n\n/**\n * Emitted when the player is muted.\n */\nexport type MutedEvent = Event;\n\n/**\n * Emitted when the player is unmuted.\n */\nexport type UnmutedEvent = Event;\n\n/**\n * Emitted when the player is ready for immediate playback, because initial audio/video\n * has been downloaded.\n */\nexport type ReadyEvent = Event;\n\n/**\n * Emitted when the player is paused.\n */\nexport interface PausedEvent extends Event {\n /**\n * The player's playback time from when this event happened.\n */\n time: number;\n}\n\n/**\n * Emitted when the player received an intention to start/resume playback.\n */\nexport interface PlayEvent extends Event {\n /**\n * The player's playback time from when this event happened.\n */\n time: number;\n}\n\n/**\n * Emitted when playback has started.\n */\nexport interface PlayingEvent extends Event {\n /**\n * The player's playback time from when this event happened.\n */\n time: number;\n}\n\n/**\n * Emitted when the playback of the current media has finished.\n */\nexport type PlaybackFinishedEvent = Event;\n\n/**\n * Source object representation the way it appears on event's payloads such as `SeekEvent`, for example.\n *\n * This interface only type hints what should be the shape of a {@link Source} object inside an event's\n * payload during runtime so it has no direct relation with the `Source` class present in `src/source.ts`.\n *\n * Do not mistake it for a `NativeInstance` type.\n */\nexport interface EventSource {\n /**\n * Event's source duration in seconds.\n */\n duration: number;\n /**\n * Whether this event's source is currently active in a player.\n */\n isActive: boolean;\n /**\n * Whether this event's source is currently attached to a player instance.\n */\n isAttachedToPlayer: boolean;\n /**\n * Metadata for this event's source.\n */\n metadata?: Record;\n /**\n * The current {@link LoadingState} of the source.\n */\n loadingState: LoadingState;\n}\n\n/**\n * Represents a seeking position.\n */\nexport interface SeekPosition {\n /**\n * The relevant {@link Source}.\n */\n source: EventSource;\n /**\n * The position within the {@link Source} in seconds.\n */\n time: number;\n}\n\n/**\n * Emitted when the player is about to seek to a new position.\n * This event only applies to VoD streams.\n * When looking for an equivalent for live streams, the {@link TimeShiftEvent} is relevant.\n */\nexport interface SeekEvent extends Event {\n /**\n * Origin source metadata.\n */\n from: SeekPosition;\n /**\n * Target source metadata.\n */\n to: SeekPosition;\n}\n\n/**\n * Emitted when seeking has finished and data to continue playback is available.\n * This event only applies to VoD streams.\n * When looking for an equivalent for live streams, the {@link TimeShiftedEvent} is relevant.\n */\nexport type SeekedEvent = Event;\n\n/**\n * Emitted when the player starts time shifting.\n * This event only applies to live streams.\n * When looking for an equivalent for VoD streams, the {@link SeekEvent} is relevant.\n */\nexport interface TimeShiftEvent extends Event {\n /**\n * The position from which we start the time shift\n */\n position: number;\n /**\n * The position to which we want to jump for the time shift\n */\n targetPosition: number;\n}\n\n/**\n * Emitted when time shifting has finished and data is available to continue playback.\n * This event only applies to live streams.\n * When looking for an equivalent for VoD streams, the {@link SeekedEvent} is relevant.\n */\nexport type TimeShiftedEvent = Event;\n\n/**\n * Emitted when the player begins to stall and to buffer due to an empty buffer.\n */\nexport type StallStartedEvent = Event;\n\n/**\n * Emitted when the player ends stalling, due to enough data in the buffer.\n */\nexport type StallEndedEvent = Event;\n\n/**\n * Emitted when the current playback time has changed.\n */\nexport interface TimeChangedEvent extends Event {\n /**\n * The player's playback time from when this event happened.\n */\n currentTime: number;\n}\n\n/**\n * Emitted when a new source loading has started.\n */\nexport interface SourceLoadEvent extends Event {\n /**\n * Source that is about to load.\n */\n source: EventSource;\n}\n\n/**\n * Emitted when a new source is loaded.\n * This does not mean that the source is immediately ready for playback.\n * {@link ReadyEvent} indicates the player is ready for immediate playback.\n */\nexport interface SourceLoadedEvent extends Event {\n /**\n * Source that was loaded into player.\n */\n source: EventSource;\n}\n\n/**\n * Emitted when the current source has been unloaded.\n */\nexport interface SourceUnloadedEvent extends Event {\n /**\n * Source that was unloaded from player.\n */\n source: EventSource;\n}\n\n/**\n * Emitted when a source error occurred.\n */\nexport type SourceErrorEvent = ErrorEvent;\n\n/**\n * Emitted when a source warning occurred.\n */\nexport type SourceWarningEvent = ErrorEvent;\n\n/**\n * Emitted when a new audio track is added to the player.\n */\nexport interface AudioAddedEvent extends Event {\n /**\n * Audio track that has been added.\n */\n audioTrack: AudioTrack;\n}\n\n/**\n * Emitted when the player's selected audio track has changed.\n */\nexport interface AudioChangedEvent extends Event {\n /**\n * Audio track that was previously selected.\n */\n oldAudioTrack: AudioTrack;\n /**\n * Audio track that is selected now.\n */\n newAudioTrack: AudioTrack;\n}\n\n/**\n * Emitted when an audio track is removed from the player.\n */\nexport interface AudioRemovedEvent extends Event {\n /**\n * Audio track that has been removed.\n */\n audioTrack: AudioTrack;\n}\n\n/**\n * Emitted when a new subtitle track is added to the player.\n */\nexport interface SubtitleAddedEvent extends Event {\n /**\n * Subtitle track that has been added.\n */\n subtitleTrack: SubtitleTrack;\n}\n\n/**\n * Emitted when a subtitle track is removed from the player.\n */\nexport interface SubtitleRemovedEvent extends Event {\n /**\n * Subtitle track that has been removed.\n */\n subtitleTrack: SubtitleTrack;\n}\n\n/**\n * Emitted when the player's selected subtitle track has changed.\n */\nexport interface SubtitleChangedEvent extends Event {\n /**\n * Subtitle track that was previously selected.\n */\n oldSubtitleTrack: SubtitleTrack;\n /**\n * Subtitle track that is selected now.\n */\n newSubtitleTrack: SubtitleTrack;\n}\n\n/**\n * Emitted when the player enters Picture in Picture mode.\n *\n * @remarks Platform: iOS, Android\n */\nexport type PictureInPictureEnterEvent = Event;\n\n/**\n * Emitted when the player exits Picture in Picture mode.\n *\n * @remarks Platform: iOS, Android\n */\nexport type PictureInPictureExitEvent = Event;\n\n/**\n * Emitted when the player has finished entering Picture in Picture mode on iOS.\n *\n * @remarks Platform: iOS\n */\nexport type PictureInPictureEnteredEvent = Event;\n\n/**\n * Emitted when the player has finished exiting Picture in Picture mode on iOS.\n *\n * @remarks Platform: iOS\n */\nexport type PictureInPictureExitedEvent = Event;\n\n/**\n * Emitted when the fullscreen functionality has been enabled.\n *\n * @remarks Platform: iOS, Android\n */\nexport type FullscreenEnabledEvent = Event;\n\n/**\n * Emitted when the fullscreen functionality has been disabled.\n *\n * @remarks Platform: iOS, Android\n */\nexport type FullscreenDisabledEvent = Event;\n\n/**\n * Emitted when the player enters fullscreen mode.\n *\n * @remarks Platform: iOS, Android\n */\nexport type FullscreenEnterEvent = Event;\n\n/**\n * Emitted when the player exits fullscreen mode.\n *\n * @remarks Platform: iOS, Android\n */\nexport type FullscreenExitEvent = Event;\n\n/**\n * Emitted when the availability of the Picture in Picture mode changed on Android.\n *\n * @remarks Platform: Android\n */\nexport interface PictureInPictureAvailabilityChangedEvent extends Event {\n /**\n * Whether Picture in Picture is available.\n */\n isPictureInPictureAvailable: boolean;\n}\n\n/**\n * Emitted when an ad break has started.\n */\nexport interface AdBreakStartedEvent extends Event {\n /**\n * The {@link AdBreak} that has started.\n */\n adBreak?: AdBreak;\n}\n\n/**\n * Emitted when an ad break has finished.\n */\nexport interface AdBreakFinishedEvent extends Event {\n /**\n * The {@link AdBreak} that has finished.\n */\n adBreak?: AdBreak;\n}\n\n/**\n * Emitted when the playback of an ad has started.\n */\nexport interface AdStartedEvent extends Event {\n /**\n * The {@link Ad} this event is related to.\n */\n ad?: Ad;\n /**\n * The target URL to open once the user clicks on the ad.\n */\n clickThroughUrl?: string;\n /**\n * The {@link AdSourceType} of the started ad.\n */\n clientType?: AdSourceType;\n /**\n * The duration of the ad in seconds.\n */\n duration: number;\n /**\n * The index of the ad in the queue.\n */\n indexInQueue: number;\n /**\n * The position of the corresponding ad.\n */\n position?: string;\n /**\n * The skip offset of the ad in seconds.\n */\n skipOffset: number;\n /**\n * The main content time at which the ad is played.\n */\n timeOffset: number;\n}\n\n/**\n * Emitted when an ad has finished playback.\n */\nexport interface AdFinishedEvent extends Event {\n /**\n * The {@link Ad} that finished playback.\n */\n ad?: Ad;\n}\n\n/**\n * Emitted when an error with the ad playback occurs.\n */\nexport interface AdErrorEvent extends ErrorEvent {\n /**\n * The {@link AdConfig} for which the ad error occurred.\n */\n adConfig?: AdConfig;\n /**\n * The {@link AdItem} for which the ad error occurred.\n */\n adItem?: AdItem;\n}\n\n/**\n * Emitted when an ad was clicked.\n */\nexport interface AdClickedEvent extends Event {\n /**\n * The click through url of the ad.\n */\n clickThroughUrl?: string;\n}\n\n/**\n * Emitted when an ad was skipped.\n */\nexport interface AdSkippedEvent extends Event {\n /**\n * The ad that was skipped.\n */\n ad?: Ad;\n}\n\n/**\n * Emitted when the playback of an ad has progressed over a quartile boundary.\n */\nexport interface AdQuartileEvent extends Event {\n /**\n * The {@link AdQuartile} boundary that playback has progressed over.\n */\n quartile: AdQuartile;\n}\n\n/**\n * Emitted when an ad manifest was successfully downloaded, parsed and added into the ad break schedule.\n */\nexport interface AdScheduledEvent extends Event {\n /**\n * The total number of scheduled ads.\n */\n numberOfAds: number;\n}\n\n/**\n * Emitted when the download of an ad manifest is started.\n */\nexport interface AdManifestLoadEvent extends Event {\n /**\n * The {@link AdBreak} this event is related to.\n */\n adBreak?: AdBreak;\n /**\n * The {@link AdConfig} of the loaded ad manifest.\n */\n adConfig?: AdConfig;\n}\n\n/**\n * Emitted when an ad manifest was successfully loaded.\n */\nexport interface AdManifestLoadedEvent extends Event {\n /**\n * The {@link AdBreak} this event is related to.\n */\n adBreak?: AdBreak;\n /**\n * The {@link AdConfig} of the loaded ad manifest.\n */\n adConfig?: AdConfig;\n /**\n * How long it took for the ad tag to be downloaded in milliseconds.\n */\n downloadTime: number;\n}\n\n/**\n * Emitted when current video download quality has changed.\n */\nexport interface VideoDownloadQualityChangedEvent extends Event {\n /**\n * The new quality\n */\n newVideoQuality: VideoQuality;\n /**\n * The previous quality\n */\n oldVideoQuality: VideoQuality;\n}\n\n/**\n * Emitted when the current video playback quality has changed.\n */\nexport interface VideoPlaybackQualityChangedEvent extends Event {\n /**\n * The new quality\n */\n newVideoQuality: VideoQuality;\n /**\n * The previous quality\n */\n oldVideoQuality: VideoQuality;\n}\n\n/**\n * Emitted when casting to a cast-compatible device is available.\n */\nexport type CastAvailableEvent = Event;\n\n/**\n * Emitted when the playback on a cast-compatible device was paused.\n *\n * On Android {@link PausedEvent} is also emitted while casting.\n */\nexport type CastPausedEvent = Event;\n\n/**\n * Emitted when the playback on a cast-compatible device has finished.\n *\n * On Android {@link PlaybackFinishedEvent} is also emitted while casting.\n */\nexport type CastPlaybackFinishedEvent = Event;\n\n/**\n * Emitted when playback on a cast-compatible device has started.\n *\n * On Android {@link PlayingEvent} is also emitted while casting.\n */\nexport type CastPlayingEvent = Event;\n\n/**\n * Emitted when the cast app is launched successfully.\n */\nexport interface CastStartedEvent extends Event {\n /**\n * The name of the cast device on which the app was launched.\n */\n deviceName: string | null;\n}\n\n/**\n * Emitted when casting is initiated, but the user still needs to choose which device should be used.\n */\nexport type CastStartEvent = Event;\n\n/**\n * Emitted when casting to a cast-compatible device is stopped.\n */\nexport type CastStoppedEvent = Event;\n\n/**\n * Emitted when the time update from the currently used cast-compatible device is received.\n */\nexport type CastTimeUpdatedEvent = Event;\n\n/**\n * Contains information for the {@link CastWaitingForDeviceEvent}.\n */\nexport interface CastPayload {\n /**\n * The current time in seconds.\n */\n currentTime: number;\n /**\n * The name of the chosen cast device.\n */\n deviceName: string | null;\n /**\n * The type of the payload (always `\"cast\"`).\n */\n type: string;\n}\n\n/**\n * Emitted when a cast-compatible device has been chosen and the player is waiting for the device to get ready for\n * playback.\n */\nexport interface CastWaitingForDeviceEvent extends Event {\n /**\n * The {@link CastPayload} object for the event\n */\n castPayload: CastPayload;\n}\n\n/**\n * Emitted when a download was finished.\n */\nexport interface DownloadFinishedEvent extends Event {\n /**\n * The time needed to finish the request, in seconds.\n */\n downloadTime: number;\n /**\n * Which type of request this was.\n */\n requestType: HttpRequestType;\n /**\n * The HTTP status code of the request.\n * If opening the connection failed, a value of `0` is returned.\n */\n httpStatus: number;\n /**\n * If the download was successful.\n */\n isSuccess: boolean;\n /**\n * The last redirect location, or `null` if no redirect happened.\n */\n lastRedirectLocation?: string;\n /**\n * The size of the downloaded data, in bytes.\n */\n size: number;\n /**\n * The URL of the request.\n */\n url: string;\n}\n\n/**\n * Emitted when the player transitions from one playback speed to another.\n * @remarks Platform: iOS, tvOS\n */\nexport interface PlaybackSpeedChangedEvent extends Event {\n /**\n * The playback speed before the change happened.\n */\n from: number;\n /**\n * The playback speed after the change happened.\n */\n to: number;\n}\n\n/**\n * Emitted when a subtitle entry transitions into the active status.\n */\nexport interface CueEnterEvent extends Event {\n /**\n * The playback time in seconds when the subtitle should be rendered.\n */\n start: number;\n /**\n * The playback time in seconds when the subtitle should be hidden.\n */\n end: number;\n /**\n * The textual content of this subtitle.\n */\n text?: string;\n /**\n * Data URI for image data of this subtitle.\n */\n image?: string;\n}\n\n/**\n * Emitted when an active subtitle entry transitions into the inactive status.\n */\nexport interface CueExitEvent extends Event {\n /**\n * The playback time in seconds when the subtitle should be rendered.\n */\n start: number;\n /**\n * The playback time in seconds when the subtitle should be hidden.\n */\n end: number;\n /**\n * The textual content of this subtitle.\n */\n text?: string;\n /**\n * Data URI for image data of this subtitle.\n */\n image?: string;\n}\n"]} \ No newline at end of file diff --git a/build/hooks/index.d.ts b/build/hooks/index.d.ts new file mode 100644 index 00000000..4832618d --- /dev/null +++ b/build/hooks/index.d.ts @@ -0,0 +1,2 @@ +export * from './usePlayer'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/build/hooks/index.d.ts.map b/build/hooks/index.d.ts.map new file mode 100644 index 00000000..ea0ebb2c --- /dev/null +++ b/build/hooks/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"} \ No newline at end of file diff --git a/build/hooks/index.js b/build/hooks/index.js new file mode 100644 index 00000000..e7457176 --- /dev/null +++ b/build/hooks/index.js @@ -0,0 +1,2 @@ +export * from './usePlayer'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/build/hooks/index.js.map b/build/hooks/index.js.map new file mode 100644 index 00000000..078486c3 --- /dev/null +++ b/build/hooks/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC","sourcesContent":["export * from './usePlayer';\n"]} \ No newline at end of file diff --git a/build/hooks/usePlayer.d.ts b/build/hooks/usePlayer.d.ts new file mode 100644 index 00000000..0a9fa0e9 --- /dev/null +++ b/build/hooks/usePlayer.d.ts @@ -0,0 +1,8 @@ +import { Player } from '../player'; +import { PlayerConfig } from '../playerConfig'; +/** + * React hook that creates and returns a reference to a `Player` instance + * that can be used inside any component. + */ +export declare function usePlayer(config?: PlayerConfig): Player; +//# sourceMappingURL=usePlayer.d.ts.map \ No newline at end of file diff --git a/build/hooks/usePlayer.d.ts.map b/build/hooks/usePlayer.d.ts.map new file mode 100644 index 00000000..64febd2a --- /dev/null +++ b/build/hooks/usePlayer.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"usePlayer.d.ts","sourceRoot":"","sources":["../../src/hooks/usePlayer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,CAEvD"} \ No newline at end of file diff --git a/build/hooks/usePlayer.js b/build/hooks/usePlayer.js new file mode 100644 index 00000000..979fc28e --- /dev/null +++ b/build/hooks/usePlayer.js @@ -0,0 +1,10 @@ +import { useRef } from 'react'; +import { Player } from '../player'; +/** + * React hook that creates and returns a reference to a `Player` instance + * that can be used inside any component. + */ +export function usePlayer(config) { + return useRef(new Player(config)).current; +} +//# sourceMappingURL=usePlayer.js.map \ No newline at end of file diff --git a/build/hooks/usePlayer.js.map b/build/hooks/usePlayer.js.map new file mode 100644 index 00000000..20a3e71e --- /dev/null +++ b/build/hooks/usePlayer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"usePlayer.js","sourceRoot":"","sources":["../../src/hooks/usePlayer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAGnC;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,MAAqB;IAC7C,OAAO,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;AAC5C,CAAC","sourcesContent":["import { useRef } from 'react';\nimport { Player } from '../player';\nimport { PlayerConfig } from '../playerConfig';\n\n/**\n * React hook that creates and returns a reference to a `Player` instance\n * that can be used inside any component.\n */\nexport function usePlayer(config?: PlayerConfig): Player {\n return useRef(new Player(config)).current;\n}\n"]} \ No newline at end of file diff --git a/build/hooks/useProxy.d.ts b/build/hooks/useProxy.d.ts new file mode 100644 index 00000000..d22ec312 --- /dev/null +++ b/build/hooks/useProxy.d.ts @@ -0,0 +1,18 @@ +import { RefObject } from 'react'; +import { Event } from '../events'; +/** + * A function that takes a generic event as argument. + */ +type Callback = (event: E) => void; +/** + * A function that takes the synthetic version of a generic event as argument. + */ +type NativeCallback = (event: { + nativeEvent: E; +}) => void; +/** + * Create a proxy function that unwraps native events. + */ +export declare function useProxy(viewRef: RefObject): (callback?: Callback) => NativeCallback; +export {}; +//# sourceMappingURL=useProxy.d.ts.map \ No newline at end of file diff --git a/build/hooks/useProxy.d.ts.map b/build/hooks/useProxy.d.ts.map new file mode 100644 index 00000000..9cb9063e --- /dev/null +++ b/build/hooks/useProxy.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"useProxy.d.ts","sourceRoot":"","sources":["../../src/hooks/useProxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAe,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAIlC;;GAEG;AACH,KAAK,QAAQ,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AAEtC;;GAEG;AACH,KAAK,cAAc,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE;IAAE,WAAW,EAAE,CAAC,CAAA;CAAE,KAAK,IAAI,CAAC;AAE7D;;GAEG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,GACtB,CAAC,CAAC,SAAS,KAAK,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,CAAC,CAchE"} \ No newline at end of file diff --git a/build/hooks/useProxy.js b/build/hooks/useProxy.js new file mode 100644 index 00000000..ef9fce81 --- /dev/null +++ b/build/hooks/useProxy.js @@ -0,0 +1,18 @@ +import { useCallback } from 'react'; +import { findNodeHandle } from 'react-native'; +import { normalizeNonFinite } from '../utils/normalizeNonFinite'; +/** + * Create a proxy function that unwraps native events. + */ +export function useProxy(viewRef) { + return useCallback((callback) => (event) => { + const eventTargetNodeHandle = event.nativeEvent.target; + if (eventTargetNodeHandle !== findNodeHandle(viewRef.current)) { + return; + } + const { target, ...eventWithoutTarget } = event.nativeEvent; + const sanitized = normalizeNonFinite(eventWithoutTarget); + callback?.(sanitized); + }, [viewRef]); +} +//# sourceMappingURL=useProxy.js.map \ No newline at end of file diff --git a/build/hooks/useProxy.js.map b/build/hooks/useProxy.js.map new file mode 100644 index 00000000..be0bd9df --- /dev/null +++ b/build/hooks/useProxy.js.map @@ -0,0 +1 @@ +{"version":3,"file":"useProxy.js","sourceRoot":"","sources":["../../src/hooks/useProxy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,WAAW,EAAE,MAAM,OAAO,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAYjE;;GAEG;AACH,MAAM,UAAU,QAAQ,CACtB,OAAuB;IAEvB,OAAO,WAAW,CAChB,CAAkB,QAAsB,EAAE,EAAE,CAC1C,CAAC,KAAyB,EAAE,EAAE;QAC5B,MAAM,qBAAqB,GAAY,KAAK,CAAC,WAAmB,CAAC,MAAM,CAAC;QACxE,IAAI,qBAAqB,KAAK,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9D,OAAO;QACT,CAAC;QACD,MAAM,EAAE,MAAM,EAAE,GAAG,kBAAkB,EAAE,GAAG,KAAK,CAAC,WAAkB,CAAC;QACnE,MAAM,SAAS,GAAG,kBAAkB,CAAC,kBAAkB,CAAC,CAAC;QACzD,QAAQ,EAAE,CAAC,SAAc,CAAC,CAAC;IAC7B,CAAC,EACH,CAAC,OAAO,CAAC,CACV,CAAC;AACJ,CAAC","sourcesContent":["import { RefObject, useCallback } from 'react';\nimport { Event } from '../events';\nimport { findNodeHandle } from 'react-native';\nimport { normalizeNonFinite } from '../utils/normalizeNonFinite';\n\n/**\n * A function that takes a generic event as argument.\n */\ntype Callback = (event: E) => void;\n\n/**\n * A function that takes the synthetic version of a generic event as argument.\n */\ntype NativeCallback = (event: { nativeEvent: E }) => void;\n\n/**\n * Create a proxy function that unwraps native events.\n */\nexport function useProxy(\n viewRef: RefObject\n): (callback?: Callback) => NativeCallback {\n return useCallback(\n (callback?: Callback) =>\n (event: { nativeEvent: E }) => {\n const eventTargetNodeHandle: number = (event.nativeEvent as any).target;\n if (eventTargetNodeHandle !== findNodeHandle(viewRef.current)) {\n return;\n }\n const { target, ...eventWithoutTarget } = event.nativeEvent as any;\n const sanitized = normalizeNonFinite(eventWithoutTarget);\n callback?.(sanitized as E);\n },\n [viewRef]\n );\n}\n"]} \ No newline at end of file diff --git a/build/index.d.ts b/build/index.d.ts new file mode 100644 index 00000000..d82de3b0 --- /dev/null +++ b/build/index.d.ts @@ -0,0 +1,32 @@ +export * from './adaptationConfig'; +export * from './advertising'; +export * from './analytics'; +export * from './audioSession'; +export * from './components'; +export * from './drm'; +export * from './events'; +export * from './hooks'; +export * from './player'; +export * from './source'; +export * from './subtitleTrack'; +export * from './styleConfig'; +export * from './ui'; +export * from './offline'; +export * from './thumbnail'; +export * from './remoteControlConfig'; +export * from './bitmovinCastManager'; +export * from './audioTrack'; +export * from './media'; +export * from './tweaksConfig'; +export * from './bufferConfig'; +export * from './playbackConfig'; +export * from './playerConfig'; +export * from './liveConfig'; +export * from './bufferApi'; +export * from './network'; +export * from './mediaControlConfig'; +export * from './debug'; +export * from './decoder/decoderConfig'; +export * from './mediaTrackRole'; +export * from './subtitleFormat'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/build/index.d.ts.map b/build/index.d.ts.map new file mode 100644 index 00000000..3e37d43e --- /dev/null +++ b/build/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,MAAM,CAAC;AACrB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC;AACrC,cAAc,SAAS,CAAC;AACxB,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC"} \ No newline at end of file diff --git a/build/index.js b/build/index.js new file mode 100644 index 00000000..b9d0f542 --- /dev/null +++ b/build/index.js @@ -0,0 +1,32 @@ +export * from './adaptationConfig'; +export * from './advertising'; +export * from './analytics'; +export * from './audioSession'; +export * from './components'; +export * from './drm'; +export * from './events'; +export * from './hooks'; +export * from './player'; +export * from './source'; +export * from './subtitleTrack'; +export * from './styleConfig'; +export * from './ui'; +export * from './offline'; +export * from './thumbnail'; +export * from './remoteControlConfig'; +export * from './bitmovinCastManager'; +export * from './audioTrack'; +export * from './media'; +export * from './tweaksConfig'; +export * from './bufferConfig'; +export * from './playbackConfig'; +export * from './playerConfig'; +export * from './liveConfig'; +export * from './bufferApi'; +export * from './network'; +export * from './mediaControlConfig'; +export * from './debug'; +export * from './decoder/decoderConfig'; +export * from './mediaTrackRole'; +export * from './subtitleFormat'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/build/index.js.map b/build/index.js.map new file mode 100644 index 00000000..a31d198b --- /dev/null +++ b/build/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,MAAM,CAAC;AACrB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,sBAAsB,CAAC;AACrC,cAAc,SAAS,CAAC;AACxB,cAAc,yBAAyB,CAAC;AACxC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC","sourcesContent":["export * from './adaptationConfig';\nexport * from './advertising';\nexport * from './analytics';\nexport * from './audioSession';\nexport * from './components';\nexport * from './drm';\nexport * from './events';\nexport * from './hooks';\nexport * from './player';\nexport * from './source';\nexport * from './subtitleTrack';\nexport * from './styleConfig';\nexport * from './ui';\nexport * from './offline';\nexport * from './thumbnail';\nexport * from './remoteControlConfig';\nexport * from './bitmovinCastManager';\nexport * from './audioTrack';\nexport * from './media';\nexport * from './tweaksConfig';\nexport * from './bufferConfig';\nexport * from './playbackConfig';\nexport * from './playerConfig';\nexport * from './liveConfig';\nexport * from './bufferApi';\nexport * from './network';\nexport * from './mediaControlConfig';\nexport * from './debug';\nexport * from './decoder/decoderConfig';\nexport * from './mediaTrackRole';\nexport * from './subtitleFormat';\n"]} \ No newline at end of file diff --git a/build/liveConfig.d.ts b/build/liveConfig.d.ts new file mode 100644 index 00000000..afc58e8d --- /dev/null +++ b/build/liveConfig.d.ts @@ -0,0 +1,13 @@ +/** + * Contains config values regarding the behaviour when playing live streams. + */ +export interface LiveConfig { + /** + * The minimum buffer depth of a stream needed to enable time shifting. + * When the internal value for the maximal possible timeshift is lower than this value, + * timeshifting should be disabled. That means `Player.maxTimeShift` returns `0` in that case. + * This value should always be non-positive value, default value is `-40`. + */ + minTimeshiftBufferDepth?: number; +} +//# sourceMappingURL=liveConfig.d.ts.map \ No newline at end of file diff --git a/build/liveConfig.d.ts.map b/build/liveConfig.d.ts.map new file mode 100644 index 00000000..c1426b23 --- /dev/null +++ b/build/liveConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"liveConfig.d.ts","sourceRoot":"","sources":["../src/liveConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC"} \ No newline at end of file diff --git a/build/liveConfig.js b/build/liveConfig.js new file mode 100644 index 00000000..38ab7054 --- /dev/null +++ b/build/liveConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=liveConfig.js.map \ No newline at end of file diff --git a/build/liveConfig.js.map b/build/liveConfig.js.map new file mode 100644 index 00000000..0d3b51ee --- /dev/null +++ b/build/liveConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"liveConfig.js","sourceRoot":"","sources":["../src/liveConfig.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Contains config values regarding the behaviour when playing live streams.\n */\nexport interface LiveConfig {\n /**\n * The minimum buffer depth of a stream needed to enable time shifting.\n * When the internal value for the maximal possible timeshift is lower than this value,\n * timeshifting should be disabled. That means `Player.maxTimeShift` returns `0` in that case.\n * This value should always be non-positive value, default value is `-40`.\n */\n minTimeshiftBufferDepth?: number;\n}\n"]} \ No newline at end of file diff --git a/build/media.d.ts b/build/media.d.ts new file mode 100644 index 00000000..8b760284 --- /dev/null +++ b/build/media.d.ts @@ -0,0 +1,85 @@ +/** + * Quality definition of a video representation. + */ +export interface VideoQuality { + /** + * The id of the media quality. + */ + id: string; + /** + * The label of the media quality that should be exposed to the user. + */ + label?: string; + /** + * The bitrate of the media quality. + */ + bitrate?: number; + /** + * The codec of the media quality. + */ + codec?: string; + /** + * The frame rate of the video quality. If the frame rate is not known or not applicable a value of -1 will be returned. + */ + frameRate?: number; + /** + * The height of the video quality. + */ + height?: number; + /** + * The width of the video quality. + */ + width?: number; +} +/** + * Quality definition of an audio representation. + * + * @platform Android + */ +export interface AudioQuality { + /** + * The id of the media quality. + */ + id: string; + /** + * The label of the media quality that should be exposed to the user. + */ + label?: string; + /** + * The bitrate in bits per second. This is the peak bitrate if known, or else the average bitrate + * if known, or else -1. + */ + bitrate?: number; + /** + * The average bitrate in bits per second, or -1 if unknown or not applicable. The + * way in which this field is populated depends on the type of media to which the format + * corresponds: + * + * - DASH representations: Always -1. + * - HLS variants: The `AVERAGE-BANDWIDTH` attribute defined on the corresponding + * `EXT-X-STREAM-INF` tag in the multivariant playlist, or -1 if not present. + * - SmoothStreaming track elements: The `Bitrate` attribute defined on the + * corresponding `TrackElement` in the manifest, or -1 if not present. + * - Progressive container formats: Often -1, but may be populated with + * the average bitrate of the container if known. + */ + averageBitrate?: number; + /** + * The peak bitrate in bits per second, or -1 if unknown or not applicable. The way + * in which this field is populated depends on the type of media to which the format corresponds: + * + * - DASH representations: The `@bandwidth` attribute of the corresponding + * `Representation` element in the manifest. + * - HLS variants: The `BANDWIDTH` attribute defined on the corresponding + * `EXT-X-STREAM-INF` tag. + * - SmoothStreaming track elements: Always -1. + * - Progressive container formats: Often -1, but may be populated with + * the peak bitrate of the container if known. + */ + peakBitrate?: number; + /** + * The codec of the media quality. + */ + codec?: string; +} +//# sourceMappingURL=media.d.ts.map \ No newline at end of file diff --git a/build/media.d.ts.map b/build/media.d.ts.map new file mode 100644 index 00000000..b6be24b5 --- /dev/null +++ b/build/media.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IACX;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IACX;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;;;;;OAYG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"} \ No newline at end of file diff --git a/build/media.js b/build/media.js new file mode 100644 index 00000000..79339166 --- /dev/null +++ b/build/media.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=media.js.map \ No newline at end of file diff --git a/build/media.js.map b/build/media.js.map new file mode 100644 index 00000000..695189ad --- /dev/null +++ b/build/media.js.map @@ -0,0 +1 @@ +{"version":3,"file":"media.js","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Quality definition of a video representation.\n */\nexport interface VideoQuality {\n /**\n * The id of the media quality.\n */\n id: string;\n /**\n * The label of the media quality that should be exposed to the user.\n */\n label?: string;\n /**\n * The bitrate of the media quality.\n */\n bitrate?: number;\n /**\n * The codec of the media quality.\n */\n codec?: string;\n /**\n * The frame rate of the video quality. If the frame rate is not known or not applicable a value of -1 will be returned.\n */\n frameRate?: number;\n /**\n * The height of the video quality.\n */\n height?: number;\n /**\n * The width of the video quality.\n */\n width?: number;\n}\n\n/**\n * Quality definition of an audio representation.\n *\n * @platform Android\n */\nexport interface AudioQuality {\n /**\n * The id of the media quality.\n */\n id: string;\n /**\n * The label of the media quality that should be exposed to the user.\n */\n label?: string;\n /**\n * The bitrate in bits per second. This is the peak bitrate if known, or else the average bitrate\n * if known, or else -1.\n */\n bitrate?: number;\n /**\n * The average bitrate in bits per second, or -1 if unknown or not applicable. The\n * way in which this field is populated depends on the type of media to which the format\n * corresponds:\n *\n * - DASH representations: Always -1.\n * - HLS variants: The `AVERAGE-BANDWIDTH` attribute defined on the corresponding\n * `EXT-X-STREAM-INF` tag in the multivariant playlist, or -1 if not present.\n * - SmoothStreaming track elements: The `Bitrate` attribute defined on the\n * corresponding `TrackElement` in the manifest, or -1 if not present.\n * - Progressive container formats: Often -1, but may be populated with\n * the average bitrate of the container if known.\n */\n averageBitrate?: number;\n /**\n * The peak bitrate in bits per second, or -1 if unknown or not applicable. The way\n * in which this field is populated depends on the type of media to which the format corresponds:\n *\n * - DASH representations: The `@bandwidth` attribute of the corresponding\n * `Representation` element in the manifest.\n * - HLS variants: The `BANDWIDTH` attribute defined on the corresponding\n * `EXT-X-STREAM-INF` tag.\n * - SmoothStreaming track elements: Always -1.\n * - Progressive container formats: Often -1, but may be populated with\n * the peak bitrate of the container if known.\n */\n peakBitrate?: number;\n /**\n * The codec of the media quality.\n */\n codec?: string;\n}\n"]} \ No newline at end of file diff --git a/build/mediaControlConfig.d.ts b/build/mediaControlConfig.d.ts new file mode 100644 index 00000000..94257fd3 --- /dev/null +++ b/build/mediaControlConfig.d.ts @@ -0,0 +1,62 @@ +/** + * Configures the media control information for the application. This information will be displayed + * wherever current media information typically appears, such as the lock screen, in notifications, and + * and inside the control center. + */ +export interface MediaControlConfig { + /** + * Enable the default behavior of displaying media information + * on the lock screen, in notifications, and within the control center. + * + * Default is `true`. + * + * For a detailed list of the supported features in the **default behavior**, + * check the **Default Supported Features** section. + * + * @remarks Enabling this flag will automatically treat {@link TweaksConfig.updatesNowPlayingInfoCenter} as `false`. + * + * ## Limitations + * --- + * - Android: If an app creates multiple player instances, the player shown in media controls is the latest one created having media controls enabled. + * - At the moment, the current media information is disabled during casting. + * + * ## Known Issues + * --- + * **iOS**: + * - There is unexpected behavior when using the IMA SDK. The Google IMA SDK adds its own commands + * for play/pause as soon as the ad starts loading (not when it starts playing). Within this window + * (approximately around 10 seconds), it is possible that both the ad and the main content are playing + * at the same time when a user interacts with the media control feature. + * + * ## Default Supported Features + * --- + * Here is the list of features supported by the default behavior. + * + * ### Populated Metadata + * - media type (to visualize the correct kind of data — _e.g. a waveform for audio files_) + * - title + * - artwork + * - elapsed time + * - duration + * + * **Android-only** + * - source description + * + * **iOS-only** + * - live or VOD status + * - playback rate + * - default playback rate + * + * ### Registered Commands + * - toggle play/pause + * - change playback position + * + * **iOS-only** + * - skip forward + * - skip backward + * - play + * - pause + */ + isEnabled?: boolean; +} +//# sourceMappingURL=mediaControlConfig.d.ts.map \ No newline at end of file diff --git a/build/mediaControlConfig.d.ts.map b/build/mediaControlConfig.d.ts.map new file mode 100644 index 00000000..890c39c4 --- /dev/null +++ b/build/mediaControlConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"mediaControlConfig.d.ts","sourceRoot":"","sources":["../src/mediaControlConfig.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAoDG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB"} \ No newline at end of file diff --git a/build/mediaControlConfig.js b/build/mediaControlConfig.js new file mode 100644 index 00000000..63eeba83 --- /dev/null +++ b/build/mediaControlConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=mediaControlConfig.js.map \ No newline at end of file diff --git a/build/mediaControlConfig.js.map b/build/mediaControlConfig.js.map new file mode 100644 index 00000000..698ed28b --- /dev/null +++ b/build/mediaControlConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"mediaControlConfig.js","sourceRoot":"","sources":["../src/mediaControlConfig.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Configures the media control information for the application. This information will be displayed\n * wherever current media information typically appears, such as the lock screen, in notifications, and\n * and inside the control center.\n */\nexport interface MediaControlConfig {\n /**\n * Enable the default behavior of displaying media information\n * on the lock screen, in notifications, and within the control center.\n *\n * Default is `true`.\n *\n * For a detailed list of the supported features in the **default behavior**,\n * check the **Default Supported Features** section.\n *\n * @remarks Enabling this flag will automatically treat {@link TweaksConfig.updatesNowPlayingInfoCenter} as `false`.\n *\n * ## Limitations\n * ---\n * - Android: If an app creates multiple player instances, the player shown in media controls is the latest one created having media controls enabled.\n * - At the moment, the current media information is disabled during casting.\n *\n * ## Known Issues\n * ---\n * **iOS**:\n * - There is unexpected behavior when using the IMA SDK. The Google IMA SDK adds its own commands\n * for play/pause as soon as the ad starts loading (not when it starts playing). Within this window\n * (approximately around 10 seconds), it is possible that both the ad and the main content are playing\n * at the same time when a user interacts with the media control feature.\n *\n * ## Default Supported Features\n * ---\n * Here is the list of features supported by the default behavior.\n *\n * ### Populated Metadata\n * - media type (to visualize the correct kind of data — _e.g. a waveform for audio files_)\n * - title\n * - artwork\n * - elapsed time\n * - duration\n *\n * **Android-only**\n * - source description\n *\n * **iOS-only**\n * - live or VOD status\n * - playback rate\n * - default playback rate\n *\n * ### Registered Commands\n * - toggle play/pause\n * - change playback position\n *\n * **iOS-only**\n * - skip forward\n * - skip backward\n * - play\n * - pause\n */\n isEnabled?: boolean;\n}\n"]} \ No newline at end of file diff --git a/build/mediaTrackRole.d.ts b/build/mediaTrackRole.d.ts new file mode 100644 index 00000000..6b06c55e --- /dev/null +++ b/build/mediaTrackRole.d.ts @@ -0,0 +1,28 @@ +/** + * The `MediaTrackRole` interface represents the role of a media track in a media stream. + */ +export interface MediaTrackRole { + /** + * The unique identifier for this role instance. + * - On Android: Corresponds to the native [`MediaTrackRole.id`](https://cdn.bitmovin.com/player/android/3/docs/player-core/com.bitmovin.player.api.media/-media-track-role/id.html). + * May be undefined. + * - On iOS and tvOS: `undefined`, as HLS characteristics do not have inherent IDs in this context. + */ + id?: string; + /** + * The URI identifying the scheme used for the role definition. + * - On Android: Corresponds to the native [`MediaTrackRole.schemeIdUri`](https://cdn.bitmovin.com/player/android/3/docs/player-core/com.bitmovin.player.api.media/-media-track-role/scheme-id-uri.html) + * (e.g., "urn:mpeg:dash:role:2011"). + * - On iOS and tvOS: predefined URN `urn:hls:characteristic` representing HLS characteristics. + */ + schemeIdUri: string; + /** + * The value of the role within the specified scheme. + * - On Android: Corresponds to the native [`MediaTrackRole.value`](https://cdn.bitmovin.com/player/android/3/docs/player-core/com.bitmovin.player.api.media/-media-track-role/value.html) + * (e.g., "main", "caption", "description"). + * - On iOS and tvOS: The raw HLS characteristic string (e.g., "public.accessibility.describes-music-and-sound", + * "public.accessibility.transcribes-spoken-dialog"). + */ + value?: string; +} +//# sourceMappingURL=mediaTrackRole.d.ts.map \ No newline at end of file diff --git a/build/mediaTrackRole.d.ts.map b/build/mediaTrackRole.d.ts.map new file mode 100644 index 00000000..5ecc1ad8 --- /dev/null +++ b/build/mediaTrackRole.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"mediaTrackRole.d.ts","sourceRoot":"","sources":["../src/mediaTrackRole.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"} \ No newline at end of file diff --git a/build/mediaTrackRole.js b/build/mediaTrackRole.js new file mode 100644 index 00000000..7edb2d84 --- /dev/null +++ b/build/mediaTrackRole.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=mediaTrackRole.js.map \ No newline at end of file diff --git a/build/mediaTrackRole.js.map b/build/mediaTrackRole.js.map new file mode 100644 index 00000000..cc1fc98f --- /dev/null +++ b/build/mediaTrackRole.js.map @@ -0,0 +1 @@ +{"version":3,"file":"mediaTrackRole.js","sourceRoot":"","sources":["../src/mediaTrackRole.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * The `MediaTrackRole` interface represents the role of a media track in a media stream.\n */\nexport interface MediaTrackRole {\n /**\n * The unique identifier for this role instance.\n * - On Android: Corresponds to the native [`MediaTrackRole.id`](https://cdn.bitmovin.com/player/android/3/docs/player-core/com.bitmovin.player.api.media/-media-track-role/id.html).\n * May be undefined.\n * - On iOS and tvOS: `undefined`, as HLS characteristics do not have inherent IDs in this context.\n */\n id?: string;\n\n /**\n * The URI identifying the scheme used for the role definition.\n * - On Android: Corresponds to the native [`MediaTrackRole.schemeIdUri`](https://cdn.bitmovin.com/player/android/3/docs/player-core/com.bitmovin.player.api.media/-media-track-role/scheme-id-uri.html)\n * (e.g., \"urn:mpeg:dash:role:2011\").\n * - On iOS and tvOS: predefined URN `urn:hls:characteristic` representing HLS characteristics.\n */\n schemeIdUri: string;\n\n /**\n * The value of the role within the specified scheme.\n * - On Android: Corresponds to the native [`MediaTrackRole.value`](https://cdn.bitmovin.com/player/android/3/docs/player-core/com.bitmovin.player.api.media/-media-track-role/value.html)\n * (e.g., \"main\", \"caption\", \"description\").\n * - On iOS and tvOS: The raw HLS characteristic string (e.g., \"public.accessibility.describes-music-and-sound\",\n * \"public.accessibility.transcribes-spoken-dialog\").\n */\n value?: string;\n}\n"]} \ No newline at end of file diff --git a/build/modules/AudioSessionModule.d.ts b/build/modules/AudioSessionModule.d.ts new file mode 100644 index 00000000..4b7d7a12 --- /dev/null +++ b/build/modules/AudioSessionModule.d.ts @@ -0,0 +1,8 @@ +import { NativeModule } from 'expo-modules-core'; +export type AudioSessionModuleEvents = Record; +declare class AudioSessionModule extends NativeModule { + setCategory(category: string): Promise; +} +declare const _default: AudioSessionModule | null; +export default _default; +//# sourceMappingURL=AudioSessionModule.d.ts.map \ No newline at end of file diff --git a/build/modules/AudioSessionModule.d.ts.map b/build/modules/AudioSessionModule.d.ts.map new file mode 100644 index 00000000..ea3323f0 --- /dev/null +++ b/build/modules/AudioSessionModule.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"AudioSessionModule.d.ts","sourceRoot":"","sources":["../../src/modules/AudioSessionModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAGtE,MAAM,MAAM,wBAAwB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE3D,OAAO,OAAO,kBAAmB,SAAQ,YAAY,CAAC,wBAAwB,CAAC;IAC7E,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAC7C;;AAGD,wBAES"} \ No newline at end of file diff --git a/build/modules/AudioSessionModule.js b/build/modules/AudioSessionModule.js new file mode 100644 index 00000000..4352d51d --- /dev/null +++ b/build/modules/AudioSessionModule.js @@ -0,0 +1,7 @@ +import { requireNativeModule } from 'expo-modules-core'; +import { Platform } from 'react-native'; +// iOS-only module +export default Platform.OS === 'ios' + ? requireNativeModule('AudioSessionModule') + : null; +//# sourceMappingURL=AudioSessionModule.js.map \ No newline at end of file diff --git a/build/modules/AudioSessionModule.js.map b/build/modules/AudioSessionModule.js.map new file mode 100644 index 00000000..7af89ea0 --- /dev/null +++ b/build/modules/AudioSessionModule.js.map @@ -0,0 +1 @@ +{"version":3,"file":"AudioSessionModule.js","sourceRoot":"","sources":["../../src/modules/AudioSessionModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAQxC,kBAAkB;AAClB,eAAe,QAAQ,CAAC,EAAE,KAAK,KAAK;IAClC,CAAC,CAAC,mBAAmB,CAAqB,oBAAoB,CAAC;IAC/D,CAAC,CAAC,IAAI,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo-modules-core';\nimport { Platform } from 'react-native';\n\nexport type AudioSessionModuleEvents = Record;\n\ndeclare class AudioSessionModule extends NativeModule {\n setCategory(category: string): Promise;\n}\n\n// iOS-only module\nexport default Platform.OS === 'ios'\n ? requireNativeModule('AudioSessionModule')\n : null;\n"]} \ No newline at end of file diff --git a/build/modules/BitmovinCastManagerModule.d.ts b/build/modules/BitmovinCastManagerModule.d.ts new file mode 100644 index 00000000..4906e041 --- /dev/null +++ b/build/modules/BitmovinCastManagerModule.d.ts @@ -0,0 +1,11 @@ +import { NativeModule } from 'expo-modules-core'; +export type BitmovinCastManagerModuleEvents = Record; +declare class BitmovinCastManagerModule extends NativeModule { + isInitialized(): Promise; + initializeCastManager(options?: Record): Promise; + sendMessage(message: string, messageNamespace?: string): Promise; + updateContext?(): Promise; +} +declare const _default: BitmovinCastManagerModule; +export default _default; +//# sourceMappingURL=BitmovinCastManagerModule.d.ts.map \ No newline at end of file diff --git a/build/modules/BitmovinCastManagerModule.d.ts.map b/build/modules/BitmovinCastManagerModule.d.ts.map new file mode 100644 index 00000000..d630521e --- /dev/null +++ b/build/modules/BitmovinCastManagerModule.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"BitmovinCastManagerModule.d.ts","sourceRoot":"","sources":["../../src/modules/BitmovinCastManagerModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAEtE,MAAM,MAAM,+BAA+B,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAElE,OAAO,OAAO,yBAA0B,SAAQ,YAAY,CAAC,+BAA+B,CAAC;IAC3F,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IACjC,qBAAqB,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IACnE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IACtE,aAAa,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;CAChC;;AAED,wBAEE"} \ No newline at end of file diff --git a/build/modules/BitmovinCastManagerModule.js b/build/modules/BitmovinCastManagerModule.js new file mode 100644 index 00000000..9bb3fcf4 --- /dev/null +++ b/build/modules/BitmovinCastManagerModule.js @@ -0,0 +1,3 @@ +import { requireNativeModule } from 'expo-modules-core'; +export default requireNativeModule('BitmovinCastManagerModule'); +//# sourceMappingURL=BitmovinCastManagerModule.js.map \ No newline at end of file diff --git a/build/modules/BitmovinCastManagerModule.js.map b/build/modules/BitmovinCastManagerModule.js.map new file mode 100644 index 00000000..7d2706b7 --- /dev/null +++ b/build/modules/BitmovinCastManagerModule.js.map @@ -0,0 +1 @@ +{"version":3,"file":"BitmovinCastManagerModule.js","sourceRoot":"","sources":["../../src/modules/BitmovinCastManagerModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAWtE,eAAe,mBAAmB,CAChC,2BAA2B,CAC5B,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo-modules-core';\n\nexport type BitmovinCastManagerModuleEvents = Record;\n\ndeclare class BitmovinCastManagerModule extends NativeModule {\n isInitialized(): Promise;\n initializeCastManager(options?: Record): Promise;\n sendMessage(message: string, messageNamespace?: string): Promise;\n updateContext?(): Promise; // Android only\n}\n\nexport default requireNativeModule(\n 'BitmovinCastManagerModule'\n);\n"]} \ No newline at end of file diff --git a/build/modules/BufferModule.d.ts b/build/modules/BufferModule.d.ts new file mode 100644 index 00000000..5d7cfea2 --- /dev/null +++ b/build/modules/BufferModule.d.ts @@ -0,0 +1,16 @@ +import { NativeModule } from 'expo-modules-core'; +import { BufferLevels } from '../bufferApi'; +export type BufferModuleEvents = Record; +declare class BufferModule extends NativeModule { + /** + * Get buffer level for the specified player and buffer type. + */ + getLevel(playerId: string, type: string): Promise; + /** + * Set target level for the specified player and buffer type. + */ + setTargetLevel(playerId: string, type: string, value: number): Promise; +} +declare const _default: BufferModule; +export default _default; +//# sourceMappingURL=BufferModule.d.ts.map \ No newline at end of file diff --git a/build/modules/BufferModule.d.ts.map b/build/modules/BufferModule.d.ts.map new file mode 100644 index 00000000..586b7f01 --- /dev/null +++ b/build/modules/BufferModule.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"BufferModule.d.ts","sourceRoot":"","sources":["../../src/modules/BufferModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAErD,OAAO,OAAO,YAAa,SAAQ,YAAY,CAAC,kBAAkB,CAAC;IACjE;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAE/D;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAC7E;;AAED,wBAAiE"} \ No newline at end of file diff --git a/build/modules/BufferModule.js b/build/modules/BufferModule.js new file mode 100644 index 00000000..303ecf7b --- /dev/null +++ b/build/modules/BufferModule.js @@ -0,0 +1,3 @@ +import { requireNativeModule } from 'expo-modules-core'; +export default requireNativeModule('BufferModule'); +//# sourceMappingURL=BufferModule.js.map \ No newline at end of file diff --git a/build/modules/BufferModule.js.map b/build/modules/BufferModule.js.map new file mode 100644 index 00000000..ee589d3e --- /dev/null +++ b/build/modules/BufferModule.js.map @@ -0,0 +1 @@ +{"version":3,"file":"BufferModule.js","sourceRoot":"","sources":["../../src/modules/BufferModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAiBtE,eAAe,mBAAmB,CAAe,cAAc,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo-modules-core';\nimport { BufferLevels } from '../bufferApi';\n\nexport type BufferModuleEvents = Record;\n\ndeclare class BufferModule extends NativeModule {\n /**\n * Get buffer level for the specified player and buffer type.\n */\n getLevel(playerId: string, type: string): Promise;\n\n /**\n * Set target level for the specified player and buffer type.\n */\n setTargetLevel(playerId: string, type: string, value: number): Promise;\n}\n\nexport default requireNativeModule('BufferModule');\n"]} \ No newline at end of file diff --git a/build/modules/DebugModule.d.ts b/build/modules/DebugModule.d.ts new file mode 100644 index 00000000..b64c705d --- /dev/null +++ b/build/modules/DebugModule.d.ts @@ -0,0 +1,8 @@ +import { NativeModule } from 'expo-modules-core'; +export type DebugModuleEvents = Record; +declare class DebugModule extends NativeModule { + setDebugLoggingEnabled(enabled: boolean): Promise; +} +declare const _default: DebugModule; +export default _default; +//# sourceMappingURL=DebugModule.d.ts.map \ No newline at end of file diff --git a/build/modules/DebugModule.d.ts.map b/build/modules/DebugModule.d.ts.map new file mode 100644 index 00000000..bd10730c --- /dev/null +++ b/build/modules/DebugModule.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"DebugModule.d.ts","sourceRoot":"","sources":["../../src/modules/DebugModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAEtE,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAEpD,OAAO,OAAO,WAAY,SAAQ,YAAY,CAAC,iBAAiB,CAAC;IAC/D,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CACxD;;AAED,wBAA+D"} \ No newline at end of file diff --git a/build/modules/DebugModule.js b/build/modules/DebugModule.js new file mode 100644 index 00000000..a5217a00 --- /dev/null +++ b/build/modules/DebugModule.js @@ -0,0 +1,3 @@ +import { requireNativeModule } from 'expo-modules-core'; +export default requireNativeModule('DebugModule'); +//# sourceMappingURL=DebugModule.js.map \ No newline at end of file diff --git a/build/modules/DebugModule.js.map b/build/modules/DebugModule.js.map new file mode 100644 index 00000000..57a96dac --- /dev/null +++ b/build/modules/DebugModule.js.map @@ -0,0 +1 @@ +{"version":3,"file":"DebugModule.js","sourceRoot":"","sources":["../../src/modules/DebugModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAQtE,eAAe,mBAAmB,CAAc,aAAa,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo-modules-core';\n\nexport type DebugModuleEvents = Record;\n\ndeclare class DebugModule extends NativeModule {\n setDebugLoggingEnabled(enabled: boolean): Promise;\n}\n\nexport default requireNativeModule('DebugModule');\n"]} \ No newline at end of file diff --git a/build/modules/PlayerModule.d.ts b/build/modules/PlayerModule.d.ts new file mode 100644 index 00000000..d2f8cb98 --- /dev/null +++ b/build/modules/PlayerModule.d.ts @@ -0,0 +1,188 @@ +import { NativeModule } from 'expo-modules-core'; +export type PlayerModuleEvents = Record; +declare class PlayerModule extends NativeModule { + /** + * Call .play() on nativeId's player. + */ + play(nativeId: string): Promise; + /** + * Call .pause() on nativeId's player. + */ + pause(nativeId: string): Promise; + /** + * Call .mute() on nativeId's player. + */ + mute(nativeId: string): Promise; + /** + * Call .unmute() on nativeId's player. + */ + unmute(nativeId: string): Promise; + /** + * Call .seek(time) on nativeId's player. + */ + seek(nativeId: string, time: number): Promise; + /** + * Sets timeShift on nativeId's player. + */ + timeShift(nativeId: string, offset: number): Promise; + /** + * Call .destroy() on nativeId's player and remove from registry. + */ + destroy(nativeId: string): Promise; + /** + * Call .setVolume(volume) on nativeId's player. + */ + setVolume(nativeId: string, volume: number): Promise; + /** + * Resolve nativeId's current volume. + */ + getVolume(nativeId: string): Promise; + /** + * Resolve nativeId's current time. + */ + currentTime(nativeId: string, mode?: string): Promise; + /** + * Resolve nativeId's current playing state. + */ + isPlaying(nativeId: string): Promise; + /** + * Resolve nativeId's current paused state. + */ + isPaused(nativeId: string): Promise; + /** + * Resolve nativeId's active source duration. + */ + duration(nativeId: string): Promise; + /** + * Resolve nativeId's current muted state. + */ + isMuted(nativeId: string): Promise; + /** + * Call .unload() on nativeId's player. + */ + unload(nativeId: string): Promise; + /** + * Resolve nativeId's current time shift value. + */ + getTimeShift(nativeId: string): Promise; + /** + * Resolve nativeId's live stream state. + */ + isLive(nativeId: string): Promise; + /** + * Resolve nativeId's maximum time shift value. + */ + getMaxTimeShift(nativeId: string): Promise; + /** + * Resolve nativeId's current playback speed. + */ + getPlaybackSpeed(nativeId: string): Promise; + /** + * Set playback speed for nativeId's player. + */ + setPlaybackSpeed(nativeId: string, playbackSpeed: number): Promise; + /** + * Resolve nativeId's current ad state. + */ + isAd(nativeId: string): Promise; + /** + * Set maximum selectable bitrate for nativeId's player. + */ + setMaxSelectableBitrate(nativeId: string, maxBitrate: number): Promise; + /** + * Resolve nativeId's AirPlay activation state (iOS only). + */ + isAirPlayActive(nativeId: string): Promise; + /** + * Resolve nativeId's AirPlay availability state (iOS only). + */ + isAirPlayAvailable(nativeId: string): Promise; + /** + * Resolve nativeId's cast availability state. + */ + isCastAvailable(nativeId: string): Promise; + /** + * Resolve nativeId's current casting state. + */ + isCasting(nativeId: string): Promise; + /** + * Initiate casting for nativeId's player. + */ + castVideo(nativeId: string): Promise; + /** + * Stop casting for nativeId's player. + */ + castStop(nativeId: string): Promise; + /** + * Skip current ad for nativeId's player. + */ + skipAd(nativeId: string): Promise; + /** + * Check if player can play at specified playback speed (iOS only). + */ + canPlayAtPlaybackSpeed(nativeId: string, playbackSpeed: number): Promise; + /** + * Creates a new Player instance using the provided config. + */ + initializeWithConfig(nativeId: string, config?: Record, networkNativeId?: string, decoderNativeId?: string): Promise; + /** + * Creates a new analytics-enabled Player instance. + */ + initializeWithAnalyticsConfig(nativeId: string, analyticsConfig: Record, config?: Record, networkNativeId?: string, decoderNativeId?: string): Promise; + /** + * Load source into the player. + * Requires SourceModule dependency. + */ + loadSource(nativeId: string, sourceNativeId: string): Promise; + /** + * Load offline content into the player. + */ + loadOfflineContent(nativeId: string, offlineContentId: string, options?: Record): Promise; + /** + * Get current audio track. + */ + getAudioTrack(nativeId: string): Promise; + /** + * Get available audio tracks. + */ + getAvailableAudioTracks(nativeId: string): Promise; + /** + * Set audio track. + */ + setAudioTrack(nativeId: string, trackId: string): Promise; + /** + * Get current subtitle track. + */ + getSubtitleTrack(nativeId: string): Promise; + /** + * Get available subtitle tracks. + */ + getAvailableSubtitles(nativeId: string): Promise; + /** + * Set subtitle track. + */ + setSubtitleTrack(nativeId: string, trackId: string): Promise; + /** + * Schedule an ad. + */ + scheduleAd(nativeId: string, adConfig: Record): Promise; + /** + * Get thumbnail for time position. + */ + getThumbnail(nativeId: string, time: number): Promise; + /** + * Get current video quality. + */ + getVideoQuality(nativeId: string): Promise; + /** + * Get available video qualities. + */ + getAvailableVideoQualities(nativeId: string): Promise; + /** + * Set video quality. + */ + setVideoQuality(nativeId: string, qualityId: string): Promise; +} +declare const _default: PlayerModule; +export default _default; +//# sourceMappingURL=PlayerModule.d.ts.map \ No newline at end of file diff --git a/build/modules/PlayerModule.d.ts.map b/build/modules/PlayerModule.d.ts.map new file mode 100644 index 00000000..c0fbef33 --- /dev/null +++ b/build/modules/PlayerModule.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"PlayerModule.d.ts","sourceRoot":"","sources":["../../src/modules/PlayerModule.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/build/modules/PlayerModule.js b/build/modules/PlayerModule.js new file mode 100644 index 00000000..9a5fe299 --- /dev/null +++ b/build/modules/PlayerModule.js @@ -0,0 +1,3 @@ +import { requireNativeModule } from 'expo-modules-core'; +export default requireNativeModule('PlayerModule'); +//# sourceMappingURL=PlayerModule.js.map \ No newline at end of file diff --git a/build/modules/PlayerModule.js.map b/build/modules/PlayerModule.js.map new file mode 100644 index 00000000..6de1855a --- /dev/null +++ b/build/modules/PlayerModule.js.map @@ -0,0 +1 @@ +{"version":3,"file":"PlayerModule.js","sourceRoot":"","sources":["../../src/modules/PlayerModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AA0PtE,eAAe,mBAAmB,CAAe,cAAc,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo-modules-core';\n\nexport type PlayerModuleEvents = Record;\n\ndeclare class PlayerModule extends NativeModule {\n /**\n * Call .play() on nativeId's player.\n */\n play(nativeId: string): Promise;\n\n /**\n * Call .pause() on nativeId's player.\n */\n pause(nativeId: string): Promise;\n\n /**\n * Call .mute() on nativeId's player.\n */\n mute(nativeId: string): Promise;\n\n /**\n * Call .unmute() on nativeId's player.\n */\n unmute(nativeId: string): Promise;\n\n /**\n * Call .seek(time) on nativeId's player.\n */\n seek(nativeId: string, time: number): Promise;\n\n /**\n * Sets timeShift on nativeId's player.\n */\n timeShift(nativeId: string, offset: number): Promise;\n\n /**\n * Call .destroy() on nativeId's player and remove from registry.\n */\n destroy(nativeId: string): Promise;\n\n /**\n * Call .setVolume(volume) on nativeId's player.\n */\n setVolume(nativeId: string, volume: number): Promise;\n\n /**\n * Resolve nativeId's current volume.\n */\n getVolume(nativeId: string): Promise;\n\n /**\n * Resolve nativeId's current time.\n */\n currentTime(nativeId: string, mode?: string): Promise;\n\n /**\n * Resolve nativeId's current playing state.\n */\n isPlaying(nativeId: string): Promise;\n\n /**\n * Resolve nativeId's current paused state.\n */\n isPaused(nativeId: string): Promise;\n\n /**\n * Resolve nativeId's active source duration.\n */\n duration(nativeId: string): Promise;\n\n /**\n * Resolve nativeId's current muted state.\n */\n isMuted(nativeId: string): Promise;\n\n /**\n * Call .unload() on nativeId's player.\n */\n unload(nativeId: string): Promise;\n\n /**\n * Resolve nativeId's current time shift value.\n */\n getTimeShift(nativeId: string): Promise;\n\n /**\n * Resolve nativeId's live stream state.\n */\n isLive(nativeId: string): Promise;\n\n /**\n * Resolve nativeId's maximum time shift value.\n */\n getMaxTimeShift(nativeId: string): Promise;\n\n /**\n * Resolve nativeId's current playback speed.\n */\n getPlaybackSpeed(nativeId: string): Promise;\n\n /**\n * Set playback speed for nativeId's player.\n */\n setPlaybackSpeed(nativeId: string, playbackSpeed: number): Promise;\n\n /**\n * Resolve nativeId's current ad state.\n */\n isAd(nativeId: string): Promise;\n\n /**\n * Set maximum selectable bitrate for nativeId's player.\n */\n setMaxSelectableBitrate(nativeId: string, maxBitrate: number): Promise;\n\n /**\n * Resolve nativeId's AirPlay activation state (iOS only).\n */\n isAirPlayActive(nativeId: string): Promise;\n\n /**\n * Resolve nativeId's AirPlay availability state (iOS only).\n */\n isAirPlayAvailable(nativeId: string): Promise;\n\n /**\n * Resolve nativeId's cast availability state.\n */\n isCastAvailable(nativeId: string): Promise;\n\n /**\n * Resolve nativeId's current casting state.\n */\n isCasting(nativeId: string): Promise;\n\n /**\n * Initiate casting for nativeId's player.\n */\n castVideo(nativeId: string): Promise;\n\n /**\n * Stop casting for nativeId's player.\n */\n castStop(nativeId: string): Promise;\n\n /**\n * Skip current ad for nativeId's player.\n */\n skipAd(nativeId: string): Promise;\n\n /**\n * Check if player can play at specified playback speed (iOS only).\n */\n canPlayAtPlaybackSpeed(\n nativeId: string,\n playbackSpeed: number\n ): Promise;\n\n /**\n * Creates a new Player instance using the provided config.\n */\n initializeWithConfig(\n nativeId: string,\n config?: Record,\n networkNativeId?: string,\n decoderNativeId?: string\n ): Promise;\n\n /**\n * Creates a new analytics-enabled Player instance.\n */\n initializeWithAnalyticsConfig(\n nativeId: string,\n analyticsConfig: Record,\n config?: Record,\n networkNativeId?: string,\n decoderNativeId?: string\n ): Promise;\n\n /**\n * Load source into the player.\n * Requires SourceModule dependency.\n */\n loadSource(nativeId: string, sourceNativeId: string): Promise;\n\n /**\n * Load offline content into the player.\n */\n loadOfflineContent(\n nativeId: string,\n offlineContentId: string,\n options?: Record\n ): Promise;\n\n /**\n * Get current audio track.\n */\n getAudioTrack(nativeId: string): Promise;\n\n /**\n * Get available audio tracks.\n */\n getAvailableAudioTracks(nativeId: string): Promise;\n\n /**\n * Set audio track.\n */\n setAudioTrack(nativeId: string, trackId: string): Promise;\n\n /**\n * Get current subtitle track.\n */\n getSubtitleTrack(nativeId: string): Promise;\n\n /**\n * Get available subtitle tracks.\n */\n getAvailableSubtitles(nativeId: string): Promise;\n\n /**\n * Set subtitle track.\n */\n setSubtitleTrack(nativeId: string, trackId: string): Promise;\n\n /**\n * Schedule an ad.\n */\n scheduleAd(nativeId: string, adConfig: Record): Promise;\n\n /**\n * Get thumbnail for time position.\n */\n getThumbnail(nativeId: string, time: number): Promise;\n\n /**\n * Get current video quality.\n */\n getVideoQuality(nativeId: string): Promise;\n\n /**\n * Get available video qualities.\n */\n getAvailableVideoQualities(nativeId: string): Promise;\n\n /**\n * Set video quality.\n */\n setVideoQuality(nativeId: string, qualityId: string): Promise;\n}\n\nexport default requireNativeModule('PlayerModule');\n"]} \ No newline at end of file diff --git a/build/modules/SourceModule.d.ts b/build/modules/SourceModule.d.ts new file mode 100644 index 00000000..fdabb3af --- /dev/null +++ b/build/modules/SourceModule.d.ts @@ -0,0 +1,51 @@ +import { NativeModule } from 'expo-modules-core'; +import { LoadingState, SourceRemoteControlConfig } from '../source'; +import { Thumbnail } from '../thumbnail'; +export type SourceModuleEvents = Record; +declare class SourceModule extends NativeModule { + /** + * Checks if the source is attached to a player. + */ + isAttachedToPlayer(nativeId: string): Promise; + /** + * Checks if the source is currently active. + */ + isActive(nativeId: string): Promise; + /** + * Initializes a source with the given nativeId, optional DRM nativeId, configuration object, + * and remote control object. + */ + initializeWithConfig(nativeId: string, drmNativeId?: string, config?: Record, remoteControl?: SourceRemoteControlConfig): Promise; + /** + * Initializes a source with the given nativeId, optional DRM nativeId, configuration object, + * remote control object, and analytics source metadata. + */ + initializeWithAnalyticsConfig(nativeId: string, drmNativeId?: string, config?: Record, remoteControl?: SourceRemoteControlConfig, analyticsSourceMetadata?: Record): Promise; + /** + * Destroys the source with the given nativeId. + */ + destroy(nativeId: string): Promise; + /** + * Returns the native DRM config reference of the source with the given nativeId. + */ + duration(nativeId: string): Promise; + /** + * Returns the current loading state of the source with the given nativeId. + */ + loadingState(nativeId: string): Promise; + /** + * Returns the metadata of the source with the given nativeId. + */ + getMetadata(nativeId: string): Promise | null>; + /** + * Sets the metadata of the source with the given nativeId. + */ + setMetadata(nativeId: string, metadata: Record | null): Promise; + /** + * Returns a thumbnail for the specified playback time. + */ + getThumbnail(nativeId: string, time: number): Promise; +} +declare const _default: SourceModule; +export default _default; +//# sourceMappingURL=SourceModule.d.ts.map \ No newline at end of file diff --git a/build/modules/SourceModule.d.ts.map b/build/modules/SourceModule.d.ts.map new file mode 100644 index 00000000..a0dac278 --- /dev/null +++ b/build/modules/SourceModule.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"SourceModule.d.ts","sourceRoot":"","sources":["../../src/modules/SourceModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAErD,OAAO,OAAO,YAAa,SAAQ,YAAY,CAAC,kBAAkB,CAAC;IACjE;;OAEG;IACH,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAE7D;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAEnD;;;OAGG;IACH,oBAAoB,CAClB,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5B,aAAa,CAAC,EAAE,yBAAyB,GACxC,OAAO,CAAC,IAAI,CAAC;IAEhB;;;OAGG;IACH,6BAA6B,CAC3B,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5B,aAAa,CAAC,EAAE,yBAAyB,EACzC,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5C,OAAO,CAAC,IAAI,CAAC;IAEhB;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAExC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAClD;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAE5D;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IAElE;;OAEG;IACH,WAAW,CACT,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GACnC,OAAO,CAAC,IAAI,CAAC;IAEhB;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;CACxE;;AAED,wBAAiE"} \ No newline at end of file diff --git a/build/modules/SourceModule.js b/build/modules/SourceModule.js new file mode 100644 index 00000000..73989a8d --- /dev/null +++ b/build/modules/SourceModule.js @@ -0,0 +1,3 @@ +import { requireNativeModule } from 'expo-modules-core'; +export default requireNativeModule('SourceModule'); +//# sourceMappingURL=SourceModule.js.map \ No newline at end of file diff --git a/build/modules/SourceModule.js.map b/build/modules/SourceModule.js.map new file mode 100644 index 00000000..d849eacb --- /dev/null +++ b/build/modules/SourceModule.js.map @@ -0,0 +1 @@ +{"version":3,"file":"SourceModule.js","sourceRoot":"","sources":["../../src/modules/SourceModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAyEtE,eAAe,mBAAmB,CAAe,cAAc,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo-modules-core';\nimport { LoadingState, SourceRemoteControlConfig } from '../source';\nimport { Thumbnail } from '../thumbnail';\n\nexport type SourceModuleEvents = Record;\n\ndeclare class SourceModule extends NativeModule {\n /**\n * Checks if the source is attached to a player.\n */\n isAttachedToPlayer(nativeId: string): Promise;\n\n /**\n * Checks if the source is currently active.\n */\n isActive(nativeId: string): Promise;\n\n /**\n * Initializes a source with the given nativeId, optional DRM nativeId, configuration object,\n * and remote control object.\n */\n initializeWithConfig(\n nativeId: string,\n drmNativeId?: string,\n config?: Record,\n remoteControl?: SourceRemoteControlConfig\n ): Promise;\n\n /**\n * Initializes a source with the given nativeId, optional DRM nativeId, configuration object,\n * remote control object, and analytics source metadata.\n */\n initializeWithAnalyticsConfig(\n nativeId: string,\n drmNativeId?: string,\n config?: Record,\n remoteControl?: SourceRemoteControlConfig,\n analyticsSourceMetadata?: Record\n ): Promise;\n\n /**\n * Destroys the source with the given nativeId.\n */\n destroy(nativeId: string): Promise;\n\n /**\n * Returns the native DRM config reference of the source with the given nativeId.\n */\n duration(nativeId: string): Promise;\n /**\n * Returns the current loading state of the source with the given nativeId.\n */\n loadingState(nativeId: string): Promise;\n\n /**\n * Returns the metadata of the source with the given nativeId.\n */\n getMetadata(nativeId: string): Promise | null>;\n\n /**\n * Sets the metadata of the source with the given nativeId.\n */\n setMetadata(\n nativeId: string,\n metadata: Record | null\n ): Promise;\n\n /**\n * Returns a thumbnail for the specified playback time.\n */\n getThumbnail(nativeId: string, time: number): Promise;\n}\n\nexport default requireNativeModule('SourceModule');\n"]} \ No newline at end of file diff --git a/build/nativeInstance.d.ts b/build/nativeInstance.d.ts new file mode 100644 index 00000000..8efbcccf --- /dev/null +++ b/build/nativeInstance.d.ts @@ -0,0 +1,49 @@ +export interface NativeInstanceConfig { + /** + * Optionally user-defined string `id` for the native instance. + * Used to access a certain native instance from any point in the source code then call + * methods/properties on it. + * + * When left empty, a random `UUIDv4` is generated for it. + * @example + * Accessing or creating the `Player` with `nativeId` equal to `my-player`: + * ``` + * const player = new Player({ nativeId: 'my-player' }) + * player.play(); // call methods and properties... + * ``` + */ + nativeId?: string; +} +export default abstract class NativeInstance { + /** + * Optionally user-defined string `id` for the native instance, or UUIDv4. + */ + readonly nativeId: string; + /** + * The configuration object used to initialize this instance. + */ + readonly config?: Config; + /** + * Generate UUID in case the user-defined `nativeId` is empty. + */ + constructor(config?: Config); + /** + * Flag indicating whether the native resources of this object have been created internally + * .i.e `initialize` has been called. + */ + abstract isInitialized: boolean; + /** + * Create the native object/resources that will be managed by this instance. + */ + abstract initialize(): void; + /** + * Flag indicating whether the native resources of this object have been disposed .i.e + * `destroy` has been called. + */ + abstract isDestroyed: boolean; + /** + * Dispose the native object/resources created by this instance during `initialize`. + */ + abstract destroy(): void; +} +//# sourceMappingURL=nativeInstance.d.ts.map \ No newline at end of file diff --git a/build/nativeInstance.d.ts.map b/build/nativeInstance.d.ts.map new file mode 100644 index 00000000..dd899f84 --- /dev/null +++ b/build/nativeInstance.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"nativeInstance.d.ts","sourceRoot":"","sources":["../src/nativeInstance.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,cAAc,CAC1C,MAAM,SAAS,oBAAoB;IAEnC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;gBACS,MAAM,CAAC,EAAE,MAAM;IAK3B;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAEhC;;OAEG;IACH,QAAQ,CAAC,UAAU,IAAI,IAAI;IAE3B;;;OAGG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAE9B;;OAEG;IACH,QAAQ,CAAC,OAAO,IAAI,IAAI;CACzB"} \ No newline at end of file diff --git a/build/nativeInstance.js b/build/nativeInstance.js new file mode 100644 index 00000000..674c3b4d --- /dev/null +++ b/build/nativeInstance.js @@ -0,0 +1,19 @@ +import * as Crypto from 'expo-crypto'; +export default class NativeInstance { + /** + * Optionally user-defined string `id` for the native instance, or UUIDv4. + */ + nativeId; + /** + * The configuration object used to initialize this instance. + */ + config; + /** + * Generate UUID in case the user-defined `nativeId` is empty. + */ + constructor(config) { + this.config = config; + this.nativeId = config?.nativeId ?? Crypto.randomUUID(); + } +} +//# sourceMappingURL=nativeInstance.js.map \ No newline at end of file diff --git a/build/nativeInstance.js.map b/build/nativeInstance.js.map new file mode 100644 index 00000000..4a58aee5 --- /dev/null +++ b/build/nativeInstance.js.map @@ -0,0 +1 @@ +{"version":3,"file":"nativeInstance.js","sourceRoot":"","sources":["../src/nativeInstance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAmBtC,MAAM,CAAC,OAAO,OAAgB,cAAc;IAG1C;;OAEG;IACM,QAAQ,CAAS;IAE1B;;OAEG;IACM,MAAM,CAAU;IAEzB;;OAEG;IACH,YAAY,MAAe;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,MAAM,EAAE,QAAQ,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;IAC1D,CAAC;CAuBF","sourcesContent":["import * as Crypto from 'expo-crypto';\n\nexport interface NativeInstanceConfig {\n /**\n * Optionally user-defined string `id` for the native instance.\n * Used to access a certain native instance from any point in the source code then call\n * methods/properties on it.\n *\n * When left empty, a random `UUIDv4` is generated for it.\n * @example\n * Accessing or creating the `Player` with `nativeId` equal to `my-player`:\n * ```\n * const player = new Player({ nativeId: 'my-player' })\n * player.play(); // call methods and properties...\n * ```\n */\n nativeId?: string;\n}\n\nexport default abstract class NativeInstance<\n Config extends NativeInstanceConfig,\n> {\n /**\n * Optionally user-defined string `id` for the native instance, or UUIDv4.\n */\n readonly nativeId: string;\n\n /**\n * The configuration object used to initialize this instance.\n */\n readonly config?: Config;\n\n /**\n * Generate UUID in case the user-defined `nativeId` is empty.\n */\n constructor(config?: Config) {\n this.config = config;\n this.nativeId = config?.nativeId ?? Crypto.randomUUID();\n }\n\n /**\n * Flag indicating whether the native resources of this object have been created internally\n * .i.e `initialize` has been called.\n */\n abstract isInitialized: boolean;\n\n /**\n * Create the native object/resources that will be managed by this instance.\n */\n abstract initialize(): void;\n\n /**\n * Flag indicating whether the native resources of this object have been disposed .i.e\n * `destroy` has been called.\n */\n abstract isDestroyed: boolean;\n\n /**\n * Dispose the native object/resources created by this instance during `initialize`.\n */\n abstract destroy(): void;\n}\n"]} \ No newline at end of file diff --git a/build/network/index.d.ts b/build/network/index.d.ts new file mode 100644 index 00000000..d0fb7e9d --- /dev/null +++ b/build/network/index.d.ts @@ -0,0 +1,50 @@ +import NativeInstance from '../nativeInstance'; +import { HttpRequestType, HttpRequest, HttpResponse, NetworkConfig } from './networkConfig'; +export { HttpRequestType, HttpRequest, HttpResponse, NetworkConfig }; +/** + * Represents a native Network configuration object. + * @internal + */ +export declare class Network extends NativeInstance { + /** + * Whether this object's native instance has been created. + */ + isInitialized: boolean; + /** + * Whether this object's native instance has been disposed. + */ + isDestroyed: boolean; + private onPreprocessHttpRequestSubscription?; + private onPreprocessHttpResponseSubscription?; + /** + * Allocates the Network config instance and its resources natively. + */ + initialize: () => Promise; + /** + * Destroys the native Network config and releases all of its allocated resources. + */ + destroy: () => Promise; + /** + * Applies the user-defined `preprocessHttpRequest` function to native's `type` and `request` data and store + * the result back in `NetworkModule`. + * + * Called from native code when `NetworkConfig.preprocessHttpRequest` is dispatched. + * + * @param requestId Passed through to identify the completion handler of the request on native. + * @param type Type of the request to be made. + * @param request The HTTP request to process. + */ + private onPreprocessHttpRequest; + /** + * Applies the user-defined `preprocessHttpResponse` function to native's `type` and `response` data and store + * the result back in `NetworkModule`. + * + * Called from native code when `NetworkConfig.preprocessHttpResponse` is dispatched. + * + * @param responseId Passed through to identify the completion handler of the response on native. + * @param type Type of the request to be made. + * @param response The HTTP response to process. + */ + private onPreprocessHttpResponse; +} +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/build/network/index.d.ts.map b/build/network/index.d.ts.map new file mode 100644 index 00000000..22962ff0 --- /dev/null +++ b/build/network/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/network/index.ts"],"names":[],"mappings":"AACA,OAAO,cAAc,MAAM,mBAAmB,CAAC;AAE/C,OAAO,EACL,eAAe,EACf,WAAW,EACX,YAAY,EACZ,aAAa,EACd,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;AAErE;;;GAGG;AACH,qBAAa,OAAQ,SAAQ,cAAc,CAAC,aAAa,CAAC;IACxD;;OAEG;IACH,aAAa,UAAS;IACtB;;OAEG;IACH,WAAW,UAAS;IAEpB,OAAO,CAAC,mCAAmC,CAAC,CAAoB;IAChE,OAAO,CAAC,oCAAoC,CAAC,CAAoB;IAEjE;;OAEG;IACH,UAAU,sBAgCR;IAEF;;OAEG;IACH,OAAO,sBASL;IAEF;;;;;;;;;OASG;IACH,OAAO,CAAC,uBAAuB,CAa7B;IAEF;;;;;;;;;OASG;IACH,OAAO,CAAC,wBAAwB,CAa9B;CACH"} \ No newline at end of file diff --git a/build/network/index.js b/build/network/index.js new file mode 100644 index 00000000..b41d5768 --- /dev/null +++ b/build/network/index.js @@ -0,0 +1,102 @@ +import NativeInstance from '../nativeInstance'; +import NetworkModule from './networkModule'; +import { HttpRequestType, } from './networkConfig'; +export { HttpRequestType }; +/** + * Represents a native Network configuration object. + * @internal + */ +export class Network extends NativeInstance { + /** + * Whether this object's native instance has been created. + */ + isInitialized = false; + /** + * Whether this object's native instance has been disposed. + */ + isDestroyed = false; + onPreprocessHttpRequestSubscription; + onPreprocessHttpResponseSubscription; + /** + * Allocates the Network config instance and its resources natively. + */ + initialize = async () => { + if (!this.isInitialized) { + console.log('Initializing Network config:', this.nativeId); + // Set up event listeners for HTTP request/response preprocessing + this.onPreprocessHttpRequestSubscription = NetworkModule.addListener('onPreprocessHttpRequest', ({ nativeId, requestId, type, request }) => { + console.log(`Received HTTP Request [${type}]:`, request); + if (nativeId !== this.nativeId) { + return; + } + this.onPreprocessHttpRequest(requestId, type, request); + }); + this.onPreprocessHttpResponseSubscription = NetworkModule.addListener('onPreprocessHttpResponse', ({ nativeId, responseId, type, response }) => { + console.log(`Received HTTP Response [${type}]:`, response); + if (nativeId !== this.nativeId) { + return; + } + this.onPreprocessHttpResponse(responseId, type, response); + }); + // Create native configuration object using Expo module + if (this.config) { + await NetworkModule.initializeWithConfig(this.nativeId, this.config); + } + this.isInitialized = true; + } + }; + /** + * Destroys the native Network config and releases all of its allocated resources. + */ + destroy = async () => { + if (!this.isDestroyed) { + await NetworkModule.destroy(this.nativeId); + this.onPreprocessHttpRequestSubscription?.remove(); + this.onPreprocessHttpResponseSubscription?.remove(); + this.onPreprocessHttpRequestSubscription = undefined; + this.onPreprocessHttpResponseSubscription = undefined; + this.isDestroyed = true; + } + }; + /** + * Applies the user-defined `preprocessHttpRequest` function to native's `type` and `request` data and store + * the result back in `NetworkModule`. + * + * Called from native code when `NetworkConfig.preprocessHttpRequest` is dispatched. + * + * @param requestId Passed through to identify the completion handler of the request on native. + * @param type Type of the request to be made. + * @param request The HTTP request to process. + */ + onPreprocessHttpRequest = (requestId, type, request) => { + this.config + ?.preprocessHttpRequest?.(type, request) + .then((resultRequest) => { + NetworkModule.setPreprocessedHttpRequest(requestId, resultRequest); + }) + .catch(() => { + NetworkModule.setPreprocessedHttpRequest(requestId, request); + }); + }; + /** + * Applies the user-defined `preprocessHttpResponse` function to native's `type` and `response` data and store + * the result back in `NetworkModule`. + * + * Called from native code when `NetworkConfig.preprocessHttpResponse` is dispatched. + * + * @param responseId Passed through to identify the completion handler of the response on native. + * @param type Type of the request to be made. + * @param response The HTTP response to process. + */ + onPreprocessHttpResponse = (responseId, type, response) => { + this.config + ?.preprocessHttpResponse?.(type, response) + .then((resultResponse) => { + NetworkModule.setPreprocessedHttpResponse(responseId, resultResponse); + }) + .catch(() => { + NetworkModule.setPreprocessedHttpResponse(responseId, response); + }); + }; +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/build/network/index.js.map b/build/network/index.js.map new file mode 100644 index 00000000..dda724ff --- /dev/null +++ b/build/network/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/network/index.ts"],"names":[],"mappings":"AACA,OAAO,cAAc,MAAM,mBAAmB,CAAC;AAC/C,OAAO,aAAa,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EACL,eAAe,GAIhB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,eAAe,EAA4C,CAAC;AAErE;;;GAGG;AACH,MAAM,OAAO,OAAQ,SAAQ,cAA6B;IACxD;;OAEG;IACH,aAAa,GAAG,KAAK,CAAC;IACtB;;OAEG;IACH,WAAW,GAAG,KAAK,CAAC;IAEZ,mCAAmC,CAAqB;IACxD,oCAAoC,CAAqB;IAEjE;;OAEG;IACH,UAAU,GAAG,KAAK,IAAI,EAAE;QACtB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACxB,OAAO,CAAC,GAAG,CAAC,8BAA8B,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3D,iEAAiE;YACjE,IAAI,CAAC,mCAAmC,GAAG,aAAa,CAAC,WAAW,CAClE,yBAAyB,EACzB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;gBACzC,OAAO,CAAC,GAAG,CAAC,0BAA0B,IAAI,IAAI,EAAE,OAAO,CAAC,CAAC;gBACzD,IAAI,QAAQ,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAC/B,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC,uBAAuB,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;YACzD,CAAC,CACF,CAAC;YAEF,IAAI,CAAC,oCAAoC,GAAG,aAAa,CAAC,WAAW,CACnE,0BAA0B,EAC1B,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE;gBAC3C,OAAO,CAAC,GAAG,CAAC,2BAA2B,IAAI,IAAI,EAAE,QAAQ,CAAC,CAAC;gBAC3D,IAAI,QAAQ,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAC/B,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC,wBAAwB,CAAC,UAAU,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;YAC5D,CAAC,CACF,CAAC;YAEF,uDAAuD;YACvD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,MAAM,aAAa,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YACvE,CAAC;YACD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC5B,CAAC;IACH,CAAC,CAAC;IAEF;;OAEG;IACH,OAAO,GAAG,KAAK,IAAI,EAAE;QACnB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3C,IAAI,CAAC,mCAAmC,EAAE,MAAM,EAAE,CAAC;YACnD,IAAI,CAAC,oCAAoC,EAAE,MAAM,EAAE,CAAC;YACpD,IAAI,CAAC,mCAAmC,GAAG,SAAS,CAAC;YACrD,IAAI,CAAC,oCAAoC,GAAG,SAAS,CAAC;YACtD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC,CAAC;IAEF;;;;;;;;;OASG;IACK,uBAAuB,GAAG,CAChC,SAAiB,EACjB,IAAqB,EACrB,OAAoB,EACpB,EAAE;QACF,IAAI,CAAC,MAAM;YACT,EAAE,qBAAqB,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC;aACvC,IAAI,CAAC,CAAC,aAAa,EAAE,EAAE;YACtB,aAAa,CAAC,0BAA0B,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QACrE,CAAC,CAAC;aACD,KAAK,CAAC,GAAG,EAAE;YACV,aAAa,CAAC,0BAA0B,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;IACP,CAAC,CAAC;IAEF;;;;;;;;;OASG;IACK,wBAAwB,GAAG,CACjC,UAAkB,EAClB,IAAqB,EACrB,QAAsB,EACtB,EAAE;QACF,IAAI,CAAC,MAAM;YACT,EAAE,sBAAsB,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC;aACzC,IAAI,CAAC,CAAC,cAAc,EAAE,EAAE;YACvB,aAAa,CAAC,2BAA2B,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;QACxE,CAAC,CAAC;aACD,KAAK,CAAC,GAAG,EAAE;YACV,aAAa,CAAC,2BAA2B,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAClE,CAAC,CAAC,CAAC;IACP,CAAC,CAAC;CACH","sourcesContent":["import { EventSubscription } from 'expo-modules-core';\nimport NativeInstance from '../nativeInstance';\nimport NetworkModule from './networkModule';\nimport {\n HttpRequestType,\n HttpRequest,\n HttpResponse,\n NetworkConfig,\n} from './networkConfig';\n\nexport { HttpRequestType, HttpRequest, HttpResponse, NetworkConfig };\n\n/**\n * Represents a native Network configuration object.\n * @internal\n */\nexport class Network extends NativeInstance {\n /**\n * Whether this object's native instance has been created.\n */\n isInitialized = false;\n /**\n * Whether this object's native instance has been disposed.\n */\n isDestroyed = false;\n\n private onPreprocessHttpRequestSubscription?: EventSubscription;\n private onPreprocessHttpResponseSubscription?: EventSubscription;\n\n /**\n * Allocates the Network config instance and its resources natively.\n */\n initialize = async () => {\n if (!this.isInitialized) {\n console.log('Initializing Network config:', this.nativeId);\n // Set up event listeners for HTTP request/response preprocessing\n this.onPreprocessHttpRequestSubscription = NetworkModule.addListener(\n 'onPreprocessHttpRequest',\n ({ nativeId, requestId, type, request }) => {\n console.log(`Received HTTP Request [${type}]:`, request);\n if (nativeId !== this.nativeId) {\n return;\n }\n this.onPreprocessHttpRequest(requestId, type, request);\n }\n );\n\n this.onPreprocessHttpResponseSubscription = NetworkModule.addListener(\n 'onPreprocessHttpResponse',\n ({ nativeId, responseId, type, response }) => {\n console.log(`Received HTTP Response [${type}]:`, response);\n if (nativeId !== this.nativeId) {\n return;\n }\n this.onPreprocessHttpResponse(responseId, type, response);\n }\n );\n\n // Create native configuration object using Expo module\n if (this.config) {\n await NetworkModule.initializeWithConfig(this.nativeId, this.config);\n }\n this.isInitialized = true;\n }\n };\n\n /**\n * Destroys the native Network config and releases all of its allocated resources.\n */\n destroy = async () => {\n if (!this.isDestroyed) {\n await NetworkModule.destroy(this.nativeId);\n this.onPreprocessHttpRequestSubscription?.remove();\n this.onPreprocessHttpResponseSubscription?.remove();\n this.onPreprocessHttpRequestSubscription = undefined;\n this.onPreprocessHttpResponseSubscription = undefined;\n this.isDestroyed = true;\n }\n };\n\n /**\n * Applies the user-defined `preprocessHttpRequest` function to native's `type` and `request` data and store\n * the result back in `NetworkModule`.\n *\n * Called from native code when `NetworkConfig.preprocessHttpRequest` is dispatched.\n *\n * @param requestId Passed through to identify the completion handler of the request on native.\n * @param type Type of the request to be made.\n * @param request The HTTP request to process.\n */\n private onPreprocessHttpRequest = (\n requestId: string,\n type: HttpRequestType,\n request: HttpRequest\n ) => {\n this.config\n ?.preprocessHttpRequest?.(type, request)\n .then((resultRequest) => {\n NetworkModule.setPreprocessedHttpRequest(requestId, resultRequest);\n })\n .catch(() => {\n NetworkModule.setPreprocessedHttpRequest(requestId, request);\n });\n };\n\n /**\n * Applies the user-defined `preprocessHttpResponse` function to native's `type` and `response` data and store\n * the result back in `NetworkModule`.\n *\n * Called from native code when `NetworkConfig.preprocessHttpResponse` is dispatched.\n *\n * @param responseId Passed through to identify the completion handler of the response on native.\n * @param type Type of the request to be made.\n * @param response The HTTP response to process.\n */\n private onPreprocessHttpResponse = (\n responseId: string,\n type: HttpRequestType,\n response: HttpResponse\n ) => {\n this.config\n ?.preprocessHttpResponse?.(type, response)\n .then((resultResponse) => {\n NetworkModule.setPreprocessedHttpResponse(responseId, resultResponse);\n })\n .catch(() => {\n NetworkModule.setPreprocessedHttpResponse(responseId, response);\n });\n };\n}\n"]} \ No newline at end of file diff --git a/build/network/networkConfig.d.ts b/build/network/networkConfig.d.ts new file mode 100644 index 00000000..756ee207 --- /dev/null +++ b/build/network/networkConfig.d.ts @@ -0,0 +1,151 @@ +import { NativeInstanceConfig } from '../nativeInstance'; +/** + * Available HTTP request types. + */ +export declare enum HttpRequestType { + ManifestDash = "manifest/dash", + ManifestHlsMaster = "manifest/hls/master", + ManifestHlsVariant = "manifest/hls/variant", + ManifestSmooth = "manifest/smooth", + MediaProgressive = "media/progressive", + MediaAudio = "media/audio", + MediaVideo = "media/video", + MediaSubtitles = "media/subtitles", + MediaThumbnails = "media/thumbnails", + DrmLicenseFairplay = "drm/license/fairplay", + DrmCertificateFairplay = "drm/certificate/fairplay", + DrmLicenseWidevine = "drm/license/widevine", + KeyHlsAes = "key/hls/aes", + Unknown = "unknown" +} +/** + * Base64-encoded string representing the HTTP request body. + */ +export type HttpRequestBody = string; +/** Represents an HTTP request. */ +export interface HttpRequest { + /** The HTTP request body to send to the server. */ + body?: HttpRequestBody; + /** + * The HTTP Headers of the request. + * Entries are expected to have the HTTP header as the key and its string content as the value. + */ + headers: Record; + /** The HTTP method of the request. */ + method: string; + /** The URL of the request. */ + url: string; +} +/** + * Base64-encoded string representing the HTTP response body. + */ +export type HttpResponseBody = string; +/** Represents an HTTP response. */ +export interface HttpResponse { + /** The HTTP response body of the response. */ + body?: HttpResponseBody; + /** + * The HTTP Headers of the response. + * Entries are expected to have the HTTP header as the key and its string content as the value. + */ + headers: Record; + /** The corresponding request object of the response. */ + request: HttpRequest; + /** The HTTP status code of the response. */ + status: number; + /** The URL of the response. May differ from {@link HttpRequest.url} when redirects have happened. */ + url: string; +} +/** + * The network API gives the ability to influence network requests. + * It enables preprocessing requests and preprocessing responses. + */ +export interface NetworkConfig extends NativeInstanceConfig { + /** + * Called before an HTTP request is made. + * Can be used to change request parameters. + * + * @param type Type of the request to be made. + * @param request The HTTP request to process. + * @returns A Promise that resolves to an `HttpRequest` object. + * - If the promise resolves, the player will use the processed request. + * - If the promise rejects, the player will fall back to using the original request. + * + * @example + * ``` + * const requestCallback = (type: HttpRequestType, request: HttpRequest) => { + * // Access current properties + * + * console.log(JSON.stringify(type)); + * console.log(JSON.stringify(request)); + * + * // Modify the request + * + * request.headers['New-Header'] = 'val'; + * request.method = 'GET'; + * + * // Return the processed request via a Promise + * + * const processed: HttpRequest = { + * body: request.body, + * headers: request.headers, + * method: request.method, + * url: request.url, + * }; + * return Promise.resolve(processed); + * }; + * + * const player = usePlayer({ + * networkConfig: { + * preprocessHttpRequest: requestCallback, + * }, + * }); + * ``` + */ + preprocessHttpRequest?: (type: HttpRequestType, request: HttpRequest) => Promise; + /** + * Called before an HTTP response is accessed by the player. + * Can be used to access or change the response. + * + * @param type Type of the corresponding request object of the response. + * @param response The HTTP response to process. + * @returns A Promise that resolves to an `HttpResponse` object. + * - If the promise resolves, the player will use the processed response. + * - If the promise rejects, the player will fall back to using the original response. + * + * @example + * ``` + * const responseCallback = (type: HttpRequestType, response: HttpResponse) => { + * // Access response properties + * + * console.log(JSON.stringify(type)); + * console.log(JSON.stringify(response)); + * + * // Modify the response + * + * response.headers['New-Header'] = 'val'; + * response.url = response.request.url; // remove eventual redirect changes + * + * // Return the processed response via a Promise + * + * const processed: HttpResponse = { + * body: response.body, + * headers: response.headers, + * request: response.request, + * status: response.status, + * url: response.url, + * }; + * return Promise.resolve(processed); + * }; + * + * // Properly attach the callback to the config + * const player = usePlayer({ + * networkConfig: { + * preprocessHttpResponse: responseCallback, + * }, + * }); + * ``` + */ + preprocessHttpResponse?: (type: HttpRequestType, response: HttpResponse) => Promise; +} +//# sourceMappingURL=networkConfig.d.ts.map \ No newline at end of file diff --git a/build/network/networkConfig.d.ts.map b/build/network/networkConfig.d.ts.map new file mode 100644 index 00000000..519cb3da --- /dev/null +++ b/build/network/networkConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"networkConfig.d.ts","sourceRoot":"","sources":["../../src/network/networkConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD;;GAEG;AACH,oBAAY,eAAe;IACzB,YAAY,kBAAkB;IAC9B,iBAAiB,wBAAwB;IACzC,kBAAkB,yBAAyB;IAC3C,cAAc,oBAAoB;IAClC,gBAAgB,sBAAsB;IACtC,UAAU,gBAAgB;IAC1B,UAAU,gBAAgB;IAC1B,cAAc,oBAAoB;IAClC,eAAe,qBAAqB;IACpC,kBAAkB,yBAAyB;IAC3C,sBAAsB,6BAA6B;IACnD,kBAAkB,yBAAyB;IAC3C,SAAS,gBAAgB;IACzB,OAAO,YAAY;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC;AAErC,kCAAkC;AAClC,MAAM,WAAW,WAAW;IAC1B,mDAAmD;IACnD,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAEtC,mCAAmC;AACnC,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,wDAAwD;IACxD,OAAO,EAAE,WAAW,CAAC;IACrB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,qGAAqG;IACrG,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,MAAM,WAAW,aAAc,SAAQ,oBAAoB;IACzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwCG;IACH,qBAAqB,CAAC,EAAE,CACtB,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,WAAW,KACjB,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0CG;IACH,sBAAsB,CAAC,EAAE,CACvB,IAAI,EAAE,eAAe,EACrB,QAAQ,EAAE,YAAY,KACnB,OAAO,CAAC,YAAY,CAAC,CAAC;CAC5B"} \ No newline at end of file diff --git a/build/network/networkConfig.js b/build/network/networkConfig.js new file mode 100644 index 00000000..f067e083 --- /dev/null +++ b/build/network/networkConfig.js @@ -0,0 +1,21 @@ +/** + * Available HTTP request types. + */ +export var HttpRequestType; +(function (HttpRequestType) { + HttpRequestType["ManifestDash"] = "manifest/dash"; + HttpRequestType["ManifestHlsMaster"] = "manifest/hls/master"; + HttpRequestType["ManifestHlsVariant"] = "manifest/hls/variant"; + HttpRequestType["ManifestSmooth"] = "manifest/smooth"; + HttpRequestType["MediaProgressive"] = "media/progressive"; + HttpRequestType["MediaAudio"] = "media/audio"; + HttpRequestType["MediaVideo"] = "media/video"; + HttpRequestType["MediaSubtitles"] = "media/subtitles"; + HttpRequestType["MediaThumbnails"] = "media/thumbnails"; + HttpRequestType["DrmLicenseFairplay"] = "drm/license/fairplay"; + HttpRequestType["DrmCertificateFairplay"] = "drm/certificate/fairplay"; + HttpRequestType["DrmLicenseWidevine"] = "drm/license/widevine"; + HttpRequestType["KeyHlsAes"] = "key/hls/aes"; + HttpRequestType["Unknown"] = "unknown"; +})(HttpRequestType || (HttpRequestType = {})); +//# sourceMappingURL=networkConfig.js.map \ No newline at end of file diff --git a/build/network/networkConfig.js.map b/build/network/networkConfig.js.map new file mode 100644 index 00000000..a275c16d --- /dev/null +++ b/build/network/networkConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"networkConfig.js","sourceRoot":"","sources":["../../src/network/networkConfig.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,CAAN,IAAY,eAeX;AAfD,WAAY,eAAe;IACzB,iDAA8B,CAAA;IAC9B,4DAAyC,CAAA;IACzC,8DAA2C,CAAA;IAC3C,qDAAkC,CAAA;IAClC,yDAAsC,CAAA;IACtC,6CAA0B,CAAA;IAC1B,6CAA0B,CAAA;IAC1B,qDAAkC,CAAA;IAClC,uDAAoC,CAAA;IACpC,8DAA2C,CAAA;IAC3C,sEAAmD,CAAA;IACnD,8DAA2C,CAAA;IAC3C,4CAAyB,CAAA;IACzB,sCAAmB,CAAA;AACrB,CAAC,EAfW,eAAe,KAAf,eAAe,QAe1B","sourcesContent":["import { NativeInstanceConfig } from '../nativeInstance';\n\n/**\n * Available HTTP request types.\n */\nexport enum HttpRequestType {\n ManifestDash = 'manifest/dash',\n ManifestHlsMaster = 'manifest/hls/master',\n ManifestHlsVariant = 'manifest/hls/variant',\n ManifestSmooth = 'manifest/smooth',\n MediaProgressive = 'media/progressive',\n MediaAudio = 'media/audio',\n MediaVideo = 'media/video',\n MediaSubtitles = 'media/subtitles',\n MediaThumbnails = 'media/thumbnails',\n DrmLicenseFairplay = 'drm/license/fairplay',\n DrmCertificateFairplay = 'drm/certificate/fairplay',\n DrmLicenseWidevine = 'drm/license/widevine',\n KeyHlsAes = 'key/hls/aes',\n Unknown = 'unknown',\n}\n\n/**\n * Base64-encoded string representing the HTTP request body.\n */\nexport type HttpRequestBody = string;\n\n/** Represents an HTTP request. */\nexport interface HttpRequest {\n /** The HTTP request body to send to the server. */\n body?: HttpRequestBody;\n /**\n * The HTTP Headers of the request.\n * Entries are expected to have the HTTP header as the key and its string content as the value.\n */\n headers: Record;\n /** The HTTP method of the request. */\n method: string;\n /** The URL of the request. */\n url: string;\n}\n\n/**\n * Base64-encoded string representing the HTTP response body.\n */\nexport type HttpResponseBody = string;\n\n/** Represents an HTTP response. */\nexport interface HttpResponse {\n /** The HTTP response body of the response. */\n body?: HttpResponseBody;\n /**\n * The HTTP Headers of the response.\n * Entries are expected to have the HTTP header as the key and its string content as the value.\n */\n headers: Record;\n /** The corresponding request object of the response. */\n request: HttpRequest;\n /** The HTTP status code of the response. */\n status: number;\n /** The URL of the response. May differ from {@link HttpRequest.url} when redirects have happened. */\n url: string;\n}\n\n/**\n * The network API gives the ability to influence network requests.\n * It enables preprocessing requests and preprocessing responses.\n */\nexport interface NetworkConfig extends NativeInstanceConfig {\n /**\n * Called before an HTTP request is made.\n * Can be used to change request parameters.\n *\n * @param type Type of the request to be made.\n * @param request The HTTP request to process.\n * @returns A Promise that resolves to an `HttpRequest` object.\n * - If the promise resolves, the player will use the processed request.\n * - If the promise rejects, the player will fall back to using the original request.\n *\n * @example\n * ```\n * const requestCallback = (type: HttpRequestType, request: HttpRequest) => {\n * // Access current properties\n *\n * console.log(JSON.stringify(type));\n * console.log(JSON.stringify(request));\n *\n * // Modify the request\n *\n * request.headers['New-Header'] = 'val';\n * request.method = 'GET';\n *\n * // Return the processed request via a Promise\n *\n * const processed: HttpRequest = {\n * body: request.body,\n * headers: request.headers,\n * method: request.method,\n * url: request.url,\n * };\n * return Promise.resolve(processed);\n * };\n *\n * const player = usePlayer({\n * networkConfig: {\n * preprocessHttpRequest: requestCallback,\n * },\n * });\n * ```\n */\n preprocessHttpRequest?: (\n type: HttpRequestType,\n request: HttpRequest\n ) => Promise;\n /**\n * Called before an HTTP response is accessed by the player.\n * Can be used to access or change the response.\n *\n * @param type Type of the corresponding request object of the response.\n * @param response The HTTP response to process.\n * @returns A Promise that resolves to an `HttpResponse` object.\n * - If the promise resolves, the player will use the processed response.\n * - If the promise rejects, the player will fall back to using the original response.\n *\n * @example\n * ```\n * const responseCallback = (type: HttpRequestType, response: HttpResponse) => {\n * // Access response properties\n *\n * console.log(JSON.stringify(type));\n * console.log(JSON.stringify(response));\n *\n * // Modify the response\n *\n * response.headers['New-Header'] = 'val';\n * response.url = response.request.url; // remove eventual redirect changes\n *\n * // Return the processed response via a Promise\n *\n * const processed: HttpResponse = {\n * body: response.body,\n * headers: response.headers,\n * request: response.request,\n * status: response.status,\n * url: response.url,\n * };\n * return Promise.resolve(processed);\n * };\n *\n * // Properly attach the callback to the config\n * const player = usePlayer({\n * networkConfig: {\n * preprocessHttpResponse: responseCallback,\n * },\n * });\n * ```\n */\n preprocessHttpResponse?: (\n type: HttpRequestType,\n response: HttpResponse\n ) => Promise;\n}\n"]} \ No newline at end of file diff --git a/build/network/networkModule.d.ts b/build/network/networkModule.d.ts new file mode 100644 index 00000000..dae2b3a8 --- /dev/null +++ b/build/network/networkModule.d.ts @@ -0,0 +1,29 @@ +import { NativeModule } from 'expo-modules-core'; +import { HttpRequest, HttpResponse, NetworkConfig, HttpRequestType } from './networkConfig'; +export type NetworkModuleEvents = { + onPreprocessHttpRequest: ({ nativeId, requestId, type, request, }: { + nativeId: string; + requestId: string; + type: HttpRequestType; + request: HttpRequest; + }) => void; + onPreprocessHttpResponse: ({ nativeId, responseId, type, response, }: { + nativeId: string; + responseId: string; + type: HttpRequestType; + response: HttpResponse; + }) => void; +}; +/** + * Native NetworkModule using Expo modules API. + * Provides modern async/await interface while maintaining backward compatibility. + */ +declare class NetworkModule extends NativeModule { + initializeWithConfig(nativeId: string, config: NetworkConfig): Promise; + destroy(nativeId: string): Promise; + setPreprocessedHttpRequest(requestId: string, request: HttpRequest): Promise; + setPreprocessedHttpResponse(responseId: string, response: HttpResponse): Promise; +} +declare const _default: NetworkModule; +export default _default; +//# sourceMappingURL=networkModule.d.ts.map \ No newline at end of file diff --git a/build/network/networkModule.d.ts.map b/build/network/networkModule.d.ts.map new file mode 100644 index 00000000..e0b6b7ed --- /dev/null +++ b/build/network/networkModule.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"networkModule.d.ts","sourceRoot":"","sources":["../../src/network/networkModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AACtE,OAAO,EACL,WAAW,EACX,YAAY,EACZ,aAAa,EACb,eAAe,EAChB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,mBAAmB,GAAG;IAChC,uBAAuB,EAAE,CAAC,EACxB,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,OAAO,GACR,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,eAAe,CAAC;QACtB,OAAO,EAAE,WAAW,CAAC;KACtB,KAAK,IAAI,CAAC;IACX,wBAAwB,EAAE,CAAC,EACzB,QAAQ,EACR,UAAU,EACV,IAAI,EACJ,QAAQ,GACT,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,eAAe,CAAC;QACtB,QAAQ,EAAE,YAAY,CAAC;KACxB,KAAK,IAAI,CAAC;CACZ,CAAC;AAEF;;;GAGG;AACH,OAAO,OAAO,aAAc,SAAQ,YAAY,CAAC,mBAAmB,CAAC;IACnE,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAC5E,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IACxC,0BAA0B,CACxB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC;IAChB,2BAA2B,CACzB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,YAAY,GACrB,OAAO,CAAC,IAAI,CAAC;CACjB;;AAED,wBAAmE"} \ No newline at end of file diff --git a/build/network/networkModule.js b/build/network/networkModule.js new file mode 100644 index 00000000..85bc3158 --- /dev/null +++ b/build/network/networkModule.js @@ -0,0 +1,3 @@ +import { requireNativeModule } from 'expo-modules-core'; +export default requireNativeModule('NetworkModule'); +//# sourceMappingURL=networkModule.js.map \ No newline at end of file diff --git a/build/network/networkModule.js.map b/build/network/networkModule.js.map new file mode 100644 index 00000000..fdf7cc38 --- /dev/null +++ b/build/network/networkModule.js.map @@ -0,0 +1 @@ +{"version":3,"file":"networkModule.js","sourceRoot":"","sources":["../../src/network/networkModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAkDtE,eAAe,mBAAmB,CAAgB,eAAe,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo-modules-core';\nimport {\n HttpRequest,\n HttpResponse,\n NetworkConfig,\n HttpRequestType,\n} from './networkConfig';\n\nexport type NetworkModuleEvents = {\n onPreprocessHttpRequest: ({\n nativeId,\n requestId,\n type,\n request,\n }: {\n nativeId: string;\n requestId: string;\n type: HttpRequestType;\n request: HttpRequest;\n }) => void;\n onPreprocessHttpResponse: ({\n nativeId,\n responseId,\n type,\n response,\n }: {\n nativeId: string;\n responseId: string;\n type: HttpRequestType;\n response: HttpResponse;\n }) => void;\n};\n\n/**\n * Native NetworkModule using Expo modules API.\n * Provides modern async/await interface while maintaining backward compatibility.\n */\ndeclare class NetworkModule extends NativeModule {\n initializeWithConfig(nativeId: string, config: NetworkConfig): Promise;\n destroy(nativeId: string): Promise;\n setPreprocessedHttpRequest(\n requestId: string,\n request: HttpRequest\n ): Promise;\n setPreprocessedHttpResponse(\n responseId: string,\n response: HttpResponse\n ): Promise;\n}\n\nexport default requireNativeModule('NetworkModule');\n"]} \ No newline at end of file diff --git a/build/offline/index.d.ts b/build/offline/index.d.ts new file mode 100644 index 00000000..8ada8b37 --- /dev/null +++ b/build/offline/index.d.ts @@ -0,0 +1,8 @@ +export * from './offlineState'; +export * from './offlineDownloadRequest'; +export * from './offlineContentConfig'; +export * from './offlineSourceOptions'; +export * from './offlineContentOptions'; +export * from './offlineContentManager'; +export * from './offlineContentManagerListener'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/build/offline/index.d.ts.map b/build/offline/index.d.ts.map new file mode 100644 index 00000000..d0faf586 --- /dev/null +++ b/build/offline/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/offline/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iCAAiC,CAAC"} \ No newline at end of file diff --git a/build/offline/index.js b/build/offline/index.js new file mode 100644 index 00000000..46c7c22f --- /dev/null +++ b/build/offline/index.js @@ -0,0 +1,8 @@ +export * from './offlineState'; +export * from './offlineDownloadRequest'; +export * from './offlineContentConfig'; +export * from './offlineSourceOptions'; +export * from './offlineContentOptions'; +export * from './offlineContentManager'; +export * from './offlineContentManagerListener'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/build/offline/index.js.map b/build/offline/index.js.map new file mode 100644 index 00000000..03d3f8f1 --- /dev/null +++ b/build/offline/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/offline/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iCAAiC,CAAC","sourcesContent":["export * from './offlineState';\nexport * from './offlineDownloadRequest';\nexport * from './offlineContentConfig';\nexport * from './offlineSourceOptions';\nexport * from './offlineContentOptions';\nexport * from './offlineContentManager';\nexport * from './offlineContentManagerListener';\n"]} \ No newline at end of file diff --git a/build/offline/offlineContentConfig.d.ts b/build/offline/offlineContentConfig.d.ts new file mode 100644 index 00000000..f27a7046 --- /dev/null +++ b/build/offline/offlineContentConfig.d.ts @@ -0,0 +1,18 @@ +import { NativeInstanceConfig } from '../nativeInstance'; +import { SourceConfig } from '../source'; +/** + * Object used to configure a new `OfflineContentManager` instance. + * @remarks Platform: Android, iOS + */ +export interface OfflineContentConfig extends NativeInstanceConfig { + /** + * An identifier for this source that is unique within the location and must never change. + * The root folder will contain a folder based on this id. + */ + identifier: string; + /** + * The `SourceConfig` used to download the offline resources. + */ + sourceConfig: SourceConfig; +} +//# sourceMappingURL=offlineContentConfig.d.ts.map \ No newline at end of file diff --git a/build/offline/offlineContentConfig.d.ts.map b/build/offline/offlineContentConfig.d.ts.map new file mode 100644 index 00000000..abc2c436 --- /dev/null +++ b/build/offline/offlineContentConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineContentConfig.d.ts","sourceRoot":"","sources":["../../src/offline/offlineContentConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC;;;GAGG;AACH,MAAM,WAAW,oBAAqB,SAAQ,oBAAoB;IAChE;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,YAAY,EAAE,YAAY,CAAC;CAC5B"} \ No newline at end of file diff --git a/build/offline/offlineContentConfig.js b/build/offline/offlineContentConfig.js new file mode 100644 index 00000000..ee9c4854 --- /dev/null +++ b/build/offline/offlineContentConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=offlineContentConfig.js.map \ No newline at end of file diff --git a/build/offline/offlineContentConfig.js.map b/build/offline/offlineContentConfig.js.map new file mode 100644 index 00000000..6b68c58d --- /dev/null +++ b/build/offline/offlineContentConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineContentConfig.js","sourceRoot":"","sources":["../../src/offline/offlineContentConfig.ts"],"names":[],"mappings":"","sourcesContent":["import { NativeInstanceConfig } from '../nativeInstance';\nimport { SourceConfig } from '../source';\n\n/**\n * Object used to configure a new `OfflineContentManager` instance.\n * @remarks Platform: Android, iOS\n */\nexport interface OfflineContentConfig extends NativeInstanceConfig {\n /**\n * An identifier for this source that is unique within the location and must never change.\n * The root folder will contain a folder based on this id.\n */\n identifier: string;\n /**\n * The `SourceConfig` used to download the offline resources.\n */\n sourceConfig: SourceConfig;\n}\n"]} \ No newline at end of file diff --git a/build/offline/offlineContentManager.d.ts b/build/offline/offlineContentManager.d.ts new file mode 100644 index 00000000..3c49fad1 --- /dev/null +++ b/build/offline/offlineContentManager.d.ts @@ -0,0 +1,90 @@ +import NativeInstance from '../nativeInstance'; +import { OfflineContentManagerListener } from './offlineContentManagerListener'; +import { OfflineContentConfig } from './offlineContentConfig'; +import { OfflineDownloadRequest } from './offlineDownloadRequest'; +import { OfflineState } from './offlineState'; +/** + * Provides the means to download and store sources locally that can be played back with a Player + * without an active network connection. An OfflineContentManager instance can be created via + * the constructor and will be idle until initialized. + * + * @remarks Platform: Android, iOS + */ +export declare class OfflineContentManager extends NativeInstance { + isInitialized: boolean; + isDestroyed: boolean; + private eventSubscription?; + private listeners; + private drm?; + /** + * Allocates the native `OfflineManager` instance and its resources natively. + * Registers the `DeviceEventEmitter` listener to receive data from the native `OfflineContentManagerListener` callbacks + */ + initialize: () => Promise; + /** + * Adds a listener to the receive data from the native `OfflineContentManagerListener` callbacks + * Returns a function that removes this listener from the `OfflineContentManager` that registered it. + */ + addListener: (listener: OfflineContentManagerListener) => (() => void); + /** + * Destroys the native `OfflineManager` and releases all of its allocated resources. + */ + destroy: () => Promise; + /** + * Gets the current state of the `OfflineContentManager` + */ + state: () => Promise; + /** + * Loads the current `OfflineContentOptions`. + * When the options are loaded the data will be passed to the `OfflineContentManagerListener.onOptionsAvailable`. + */ + getOptions: () => Promise; + /** + * Enqueues downloads according to the `OfflineDownloadRequest`. + * The promise will reject in the event of null or invalid request parameters. + * The promise will reject when calling this method when download has already started or is completed. + * The promise will resolve when the download has been queued. The download will is not finished when the promise resolves. + */ + download: (request: OfflineDownloadRequest) => Promise; + /** + * Resumes all suspended actions. + */ + resume: () => Promise; + /** + * Suspends all active actions. + */ + suspend: () => Promise; + /** + * Cancels and deletes the active download. + */ + cancelDownload: () => Promise; + /** + * Resolves how many bytes of storage are used by the offline content. + */ + usedStorage: () => Promise; + /** + * Deletes everything related to the related content ID. + */ + deleteAll: () => Promise; + /** + * Downloads the offline license. + * When finished successfully, data will be passed to the `OfflineContentManagerListener.onDrmLicenseUpdated`. + * Errors are transmitted to the `OfflineContentManagerListener.onError`. + */ + downloadLicense: () => Promise; + /** + * Releases the currently held offline license. + * When finished successfully data will be passed to the `OfflineContentManagerListener.onDrmLicenseUpdated`. + * Errors are transmitted to the `OfflineContentManagerListener.onError`. + * + * @remarks Platform: Android + */ + releaseLicense: () => Promise; + /** + * Renews the already downloaded DRM license. + * When finished successfully data will be passed to the `OfflineContentManagerListener.onDrmLicenseUpdated`. + * Errors are transmitted to the `OfflineContentManagerListener.onError`. + */ + renewOfflineLicense: () => Promise; +} +//# sourceMappingURL=offlineContentManager.d.ts.map \ No newline at end of file diff --git a/build/offline/offlineContentManager.d.ts.map b/build/offline/offlineContentManager.d.ts.map new file mode 100644 index 00000000..6145b2aa --- /dev/null +++ b/build/offline/offlineContentManager.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineContentManager.d.ts","sourceRoot":"","sources":["../../src/offline/offlineContentManager.ts"],"names":[],"mappings":"AACA,OAAO,cAAc,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAEL,6BAA6B,EAE9B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAiC9C;;;;;;GAMG;AACH,qBAAa,qBAAsB,SAAQ,cAAc,CAAC,oBAAoB,CAAC;IAC7E,aAAa,UAAS;IACtB,WAAW,UAAS;IACpB,OAAO,CAAC,iBAAiB,CAAC,CAAoB;IAC9C,OAAO,CAAC,SAAS,CAC0B;IAC3C,OAAO,CAAC,GAAG,CAAC,CAAM;IAElB;;;OAGG;IACH,UAAU,QAAa,OAAO,CAAC,IAAI,CAAC,CA8BlC;IAEF;;;OAGG;IACH,WAAW,GAAI,UAAU,6BAA6B,KAAG,CAAC,MAAM,IAAI,CAAC,CAMnE;IAEF;;OAEG;IACH,OAAO,QAAa,OAAO,CAAC,IAAI,CAAC,CAW/B;IAEF;;OAEG;IACH,KAAK,QAAa,OAAO,CAAC,YAAY,CAAC,CAErC;IAEF;;;OAGG;IACH,UAAU,QAAa,OAAO,CAAC,IAAI,CAAC,CAElC;IAEF;;;;;OAKG;IACH,QAAQ,GAAU,SAAS,sBAAsB,KAAG,OAAO,CAAC,IAAI,CAAC,CAE/D;IAEF;;OAEG;IACH,MAAM,QAAa,OAAO,CAAC,IAAI,CAAC,CAE9B;IAEF;;OAEG;IACH,OAAO,QAAa,OAAO,CAAC,IAAI,CAAC,CAE/B;IAEF;;OAEG;IACH,cAAc,QAAa,OAAO,CAAC,IAAI,CAAC,CAEtC;IAEF;;OAEG;IACH,WAAW,QAAa,OAAO,CAAC,MAAM,CAAC,CAErC;IAEF;;OAEG;IACH,SAAS,QAAa,OAAO,CAAC,IAAI,CAAC,CAEjC;IAEF;;;;OAIG;IACH,eAAe,QAAa,OAAO,CAAC,IAAI,CAAC,CAEvC;IAEF;;;;;;OAMG;IACH,cAAc,QAAa,OAAO,CAAC,IAAI,CAAC,CAEtC;IAEF;;;;OAIG;IACH,mBAAmB,QAAa,OAAO,CAAC,IAAI,CAAC,CAE3C;CACH"} \ No newline at end of file diff --git a/build/offline/offlineContentManager.js b/build/offline/offlineContentManager.js new file mode 100644 index 00000000..1e03d2f0 --- /dev/null +++ b/build/offline/offlineContentManager.js @@ -0,0 +1,177 @@ +import NativeInstance from '../nativeInstance'; +import { OfflineEventType, } from './offlineContentManagerListener'; +import { Drm } from '../drm'; +import OfflineModule from './offlineModule'; +const handleBitmovinNativeOfflineEvent = (data, listeners) => { + listeners.forEach((listener) => { + if (!listener) + return; + if (data.eventType === OfflineEventType.onCompleted) { + listener.onCompleted?.(data); + } + else if (data.eventType === OfflineEventType.onError) { + listener.onError?.(data); + } + else if (data.eventType === OfflineEventType.onProgress) { + listener.onProgress?.(data); + } + else if (data.eventType === OfflineEventType.onOptionsAvailable) { + listener.onOptionsAvailable?.(data); + } + else if (data.eventType === OfflineEventType.onDrmLicenseUpdated) { + listener.onDrmLicenseUpdated?.(data); + } + else if (data.eventType === OfflineEventType.onDrmLicenseExpired) { + listener.onDrmLicenseExpired?.(data); + } + else if (data.eventType === OfflineEventType.onSuspended) { + listener.onSuspended?.(data); + } + else if (data.eventType === OfflineEventType.onResumed) { + listener.onResumed?.(data); + } + else if (data.eventType === OfflineEventType.onCanceled) { + listener.onCanceled?.(data); + } + }); +}; +/** + * Provides the means to download and store sources locally that can be played back with a Player + * without an active network connection. An OfflineContentManager instance can be created via + * the constructor and will be idle until initialized. + * + * @remarks Platform: Android, iOS + */ +export class OfflineContentManager extends NativeInstance { + isInitialized = false; + isDestroyed = false; + eventSubscription; + listeners = new Set(); + drm; + /** + * Allocates the native `OfflineManager` instance and its resources natively. + * Registers the `DeviceEventEmitter` listener to receive data from the native `OfflineContentManagerListener` callbacks + */ + initialize = async () => { + if (!this.isInitialized && this.config) { + this.eventSubscription = OfflineModule.addListener('onBitmovinOfflineEvent', (event) => { + if (this.nativeId !== event.nativeId) { + return; + } + handleBitmovinNativeOfflineEvent(event, this.listeners); + }); + if (this.config.sourceConfig.drmConfig) { + this.drm = new Drm(this.config.sourceConfig.drmConfig); + await this.drm.initialize(); + } + await OfflineModule.initializeWithConfig(this.nativeId, { + identifier: this.config.identifier, + sourceConfig: this.config.sourceConfig, + }, this.drm?.nativeId); + } + this.isInitialized = true; + return Promise.resolve(); + }; + /** + * Adds a listener to the receive data from the native `OfflineContentManagerListener` callbacks + * Returns a function that removes this listener from the `OfflineContentManager` that registered it. + */ + addListener = (listener) => { + this.listeners.add(listener); + return () => { + this.listeners.delete(listener); + }; + }; + /** + * Destroys the native `OfflineManager` and releases all of its allocated resources. + */ + destroy = async () => { + if (!this.isDestroyed) { + this.isDestroyed = true; + this.eventSubscription?.remove?.(); + this.listeners.clear(); + this.drm?.destroy(); + return OfflineModule.release(this.nativeId); + } + return Promise.resolve(); + }; + /** + * Gets the current state of the `OfflineContentManager` + */ + state = async () => { + return OfflineModule.getState(this.nativeId); + }; + /** + * Loads the current `OfflineContentOptions`. + * When the options are loaded the data will be passed to the `OfflineContentManagerListener.onOptionsAvailable`. + */ + getOptions = async () => { + return OfflineModule.getOptions(this.nativeId); + }; + /** + * Enqueues downloads according to the `OfflineDownloadRequest`. + * The promise will reject in the event of null or invalid request parameters. + * The promise will reject when calling this method when download has already started or is completed. + * The promise will resolve when the download has been queued. The download will is not finished when the promise resolves. + */ + download = async (request) => { + return OfflineModule.download(this.nativeId, request); + }; + /** + * Resumes all suspended actions. + */ + resume = async () => { + return OfflineModule.resume(this.nativeId); + }; + /** + * Suspends all active actions. + */ + suspend = async () => { + return OfflineModule.suspend(this.nativeId); + }; + /** + * Cancels and deletes the active download. + */ + cancelDownload = async () => { + return OfflineModule.cancelDownload(this.nativeId); + }; + /** + * Resolves how many bytes of storage are used by the offline content. + */ + usedStorage = async () => { + return OfflineModule.usedStorage(this.nativeId); + }; + /** + * Deletes everything related to the related content ID. + */ + deleteAll = async () => { + return OfflineModule.deleteAll(this.nativeId); + }; + /** + * Downloads the offline license. + * When finished successfully, data will be passed to the `OfflineContentManagerListener.onDrmLicenseUpdated`. + * Errors are transmitted to the `OfflineContentManagerListener.onError`. + */ + downloadLicense = async () => { + return OfflineModule.downloadLicense(this.nativeId); + }; + /** + * Releases the currently held offline license. + * When finished successfully data will be passed to the `OfflineContentManagerListener.onDrmLicenseUpdated`. + * Errors are transmitted to the `OfflineContentManagerListener.onError`. + * + * @remarks Platform: Android + */ + releaseLicense = async () => { + return OfflineModule.releaseLicense(this.nativeId); + }; + /** + * Renews the already downloaded DRM license. + * When finished successfully data will be passed to the `OfflineContentManagerListener.onDrmLicenseUpdated`. + * Errors are transmitted to the `OfflineContentManagerListener.onError`. + */ + renewOfflineLicense = async () => { + return OfflineModule.renewOfflineLicense(this.nativeId); + }; +} +//# sourceMappingURL=offlineContentManager.js.map \ No newline at end of file diff --git a/build/offline/offlineContentManager.js.map b/build/offline/offlineContentManager.js.map new file mode 100644 index 00000000..4e593a79 --- /dev/null +++ b/build/offline/offlineContentManager.js.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineContentManager.js","sourceRoot":"","sources":["../../src/offline/offlineContentManager.ts"],"names":[],"mappings":"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","sourcesContent":["import { EventSubscription } from 'expo-modules-core';\nimport NativeInstance from '../nativeInstance';\nimport {\n BitmovinNativeOfflineEventData,\n OfflineContentManagerListener,\n OfflineEventType,\n} from './offlineContentManagerListener';\nimport { OfflineContentConfig } from './offlineContentConfig';\nimport { OfflineDownloadRequest } from './offlineDownloadRequest';\nimport { OfflineState } from './offlineState';\nimport { Drm } from '../drm';\nimport OfflineModule from './offlineModule';\n\nconst handleBitmovinNativeOfflineEvent = (\n data: BitmovinNativeOfflineEventData,\n listeners: Set\n) => {\n listeners.forEach((listener) => {\n if (!listener) return;\n\n if (data.eventType === OfflineEventType.onCompleted) {\n listener.onCompleted?.(data);\n } else if (data.eventType === OfflineEventType.onError) {\n listener.onError?.(data);\n } else if (data.eventType === OfflineEventType.onProgress) {\n listener.onProgress?.(data);\n } else if (data.eventType === OfflineEventType.onOptionsAvailable) {\n listener.onOptionsAvailable?.(data);\n } else if (data.eventType === OfflineEventType.onDrmLicenseUpdated) {\n listener.onDrmLicenseUpdated?.(data);\n } else if (data.eventType === OfflineEventType.onDrmLicenseExpired) {\n listener.onDrmLicenseExpired?.(data);\n } else if (data.eventType === OfflineEventType.onSuspended) {\n listener.onSuspended?.(data);\n } else if (data.eventType === OfflineEventType.onResumed) {\n listener.onResumed?.(data);\n } else if (data.eventType === OfflineEventType.onCanceled) {\n listener.onCanceled?.(data);\n }\n });\n};\n\n/**\n * Provides the means to download and store sources locally that can be played back with a Player\n * without an active network connection. An OfflineContentManager instance can be created via\n * the constructor and will be idle until initialized.\n *\n * @remarks Platform: Android, iOS\n */\nexport class OfflineContentManager extends NativeInstance {\n isInitialized = false;\n isDestroyed = false;\n private eventSubscription?: EventSubscription;\n private listeners: Set =\n new Set();\n private drm?: Drm;\n\n /**\n * Allocates the native `OfflineManager` instance and its resources natively.\n * Registers the `DeviceEventEmitter` listener to receive data from the native `OfflineContentManagerListener` callbacks\n */\n initialize = async (): Promise => {\n if (!this.isInitialized && this.config) {\n this.eventSubscription = OfflineModule.addListener(\n 'onBitmovinOfflineEvent',\n (event: BitmovinNativeOfflineEventData) => {\n if (this.nativeId !== event.nativeId) {\n return;\n }\n\n handleBitmovinNativeOfflineEvent(event, this.listeners);\n }\n );\n\n if (this.config.sourceConfig.drmConfig) {\n this.drm = new Drm(this.config.sourceConfig.drmConfig);\n await this.drm.initialize();\n }\n\n await OfflineModule.initializeWithConfig(\n this.nativeId,\n {\n identifier: this.config.identifier,\n sourceConfig: this.config.sourceConfig,\n },\n this.drm?.nativeId\n );\n }\n\n this.isInitialized = true;\n return Promise.resolve();\n };\n\n /**\n * Adds a listener to the receive data from the native `OfflineContentManagerListener` callbacks\n * Returns a function that removes this listener from the `OfflineContentManager` that registered it.\n */\n addListener = (listener: OfflineContentManagerListener): (() => void) => {\n this.listeners.add(listener);\n\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n /**\n * Destroys the native `OfflineManager` and releases all of its allocated resources.\n */\n destroy = async (): Promise => {\n if (!this.isDestroyed) {\n this.isDestroyed = true;\n this.eventSubscription?.remove?.();\n this.listeners.clear();\n this.drm?.destroy();\n\n return OfflineModule.release(this.nativeId);\n }\n\n return Promise.resolve();\n };\n\n /**\n * Gets the current state of the `OfflineContentManager`\n */\n state = async (): Promise => {\n return OfflineModule.getState(this.nativeId) as Promise;\n };\n\n /**\n * Loads the current `OfflineContentOptions`.\n * When the options are loaded the data will be passed to the `OfflineContentManagerListener.onOptionsAvailable`.\n */\n getOptions = async (): Promise => {\n return OfflineModule.getOptions(this.nativeId);\n };\n\n /**\n * Enqueues downloads according to the `OfflineDownloadRequest`.\n * The promise will reject in the event of null or invalid request parameters.\n * The promise will reject when calling this method when download has already started or is completed.\n * The promise will resolve when the download has been queued. The download will is not finished when the promise resolves.\n */\n download = async (request: OfflineDownloadRequest): Promise => {\n return OfflineModule.download(this.nativeId, request);\n };\n\n /**\n * Resumes all suspended actions.\n */\n resume = async (): Promise => {\n return OfflineModule.resume(this.nativeId);\n };\n\n /**\n * Suspends all active actions.\n */\n suspend = async (): Promise => {\n return OfflineModule.suspend(this.nativeId);\n };\n\n /**\n * Cancels and deletes the active download.\n */\n cancelDownload = async (): Promise => {\n return OfflineModule.cancelDownload(this.nativeId);\n };\n\n /**\n * Resolves how many bytes of storage are used by the offline content.\n */\n usedStorage = async (): Promise => {\n return OfflineModule.usedStorage(this.nativeId);\n };\n\n /**\n * Deletes everything related to the related content ID.\n */\n deleteAll = async (): Promise => {\n return OfflineModule.deleteAll(this.nativeId);\n };\n\n /**\n * Downloads the offline license.\n * When finished successfully, data will be passed to the `OfflineContentManagerListener.onDrmLicenseUpdated`.\n * Errors are transmitted to the `OfflineContentManagerListener.onError`.\n */\n downloadLicense = async (): Promise => {\n return OfflineModule.downloadLicense(this.nativeId);\n };\n\n /**\n * Releases the currently held offline license.\n * When finished successfully data will be passed to the `OfflineContentManagerListener.onDrmLicenseUpdated`.\n * Errors are transmitted to the `OfflineContentManagerListener.onError`.\n *\n * @remarks Platform: Android\n */\n releaseLicense = async (): Promise => {\n return OfflineModule.releaseLicense(this.nativeId);\n };\n\n /**\n * Renews the already downloaded DRM license.\n * When finished successfully data will be passed to the `OfflineContentManagerListener.onDrmLicenseUpdated`.\n * Errors are transmitted to the `OfflineContentManagerListener.onError`.\n */\n renewOfflineLicense = async (): Promise => {\n return OfflineModule.renewOfflineLicense(this.nativeId);\n };\n}\n"]} \ No newline at end of file diff --git a/build/offline/offlineContentManagerListener.d.ts b/build/offline/offlineContentManagerListener.d.ts new file mode 100644 index 00000000..48c87047 --- /dev/null +++ b/build/offline/offlineContentManagerListener.d.ts @@ -0,0 +1,174 @@ +import { OfflineContentOptions } from './offlineContentOptions'; +import { OfflineState } from './offlineState'; +/** + * Enum to hold the `eventType` on the `BitmovinNativeOfflineEventData` + * @remarks Platform: Android, iOS + */ +export declare enum OfflineEventType { + onCompleted = "onCompleted", + onError = "onError", + onProgress = "onProgress", + onOptionsAvailable = "onOptionsAvailable", + onDrmLicenseUpdated = "onDrmLicenseUpdated", + onDrmLicenseExpired = "onDrmLicenseExpired", + onSuspended = "onSuspended", + onResumed = "onResumed", + onCanceled = "onCanceled" +} +/** + * The base interface for all offline events. + * @remarks Platform: Android, iOS + */ +export interface OfflineEvent { + /** + * The native id associated with the `OfflineContentManager` emitting this event + */ + nativeId: string; + /** + * The supplied id representing the source associated with the `OfflineContentManager` emitting this event. + */ + identifier: string; + /** + * The `OfflineEventType` that correlates to which native `OfflineContentManagerListener` method was called. + */ + eventType: T; + /** + * The current offline download state + */ + state: OfflineState; +} +/** + * Emitted when the download process has completed. + * @remarks Platform: Android, iOS + */ +export interface OnCompletedEvent extends OfflineEvent { + /** + * The options that are available to download + */ + options?: OfflineContentOptions; +} +/** + * Emitted when an error has occurred. + * @remarks Platform: Android, iOS + */ +export interface OnErrorEvent extends OfflineEvent { + /** + * The error code of the process error + */ + code?: number; + /** + * The error message of the process error + */ + message?: string; +} +/** + * Emitted when there is a progress change for the process call. + * @remarks Platform: Android, iOS + */ +export interface OnProgressEvent extends OfflineEvent { + /** + * The progress for the current process + */ + progress: number; +} +/** + * Emitted when the `OfflineContentOptions` is available after a `OfflineContentManager.getOptions` call. + * @remarks Platform: Android, iOS + */ +export interface OnOptionsAvailableEvent extends OfflineEvent { + /** + * The options that are available to download + */ + options?: OfflineContentOptions; +} +/** + * Emitted when the DRM license was updated. + * @remarks Platform: Android, iOS + */ +export type OnDrmLicenseUpdatedEvent = OfflineEvent; +/** + * Emitted when the DRM license has expired. + * @remarks Platform: iOS + */ +export type OnDrmLicenseExpiredEvent = OfflineEvent; +/** + * Emitted when all active actions have been suspended. + * @remarks Platform: Android, iOS + */ +export type OnSuspendedEvent = OfflineEvent; +/** + * Emitted when all actions have been resumed. + * @remarks Platform: Android, iOS + */ +export type OnResumedEvent = OfflineEvent; +/** + * Emitted when the download of the media content was canceled by the user and all partially downloaded content has been deleted from disk. + * @remarks Platform: Android, iOS + */ +export type OnCanceledEvent = OfflineEvent; +/** + * The type aggregation for all possible native offline events received from the `DeviceEventEmitter` + * @remarks Platform: Android, iOS + */ +export type BitmovinNativeOfflineEventData = OnCompletedEvent | OnOptionsAvailableEvent | OnProgressEvent | OnErrorEvent | OnDrmLicenseUpdatedEvent | OnDrmLicenseExpiredEvent | OnSuspendedEvent | OnResumedEvent | OnCanceledEvent; +/** + * The listener that can be passed to the `OfflineContentManager` to receive callbacks for different events. + * @remarks Platform: Android, iOS + */ +export interface OfflineContentManagerListener { + /** + * Emitted when the download process has completed. + * + * @param e The `OnCompletedEvent` that was emitted + */ + onCompleted?: (e: OnCompletedEvent) => void; + /** + * Emitted when an error has occurred. + * + * @param e The `OnErrorEvent` that was emitted + */ + onError?: (e: OnErrorEvent) => void; + /** + * Emitted when there is a progress change for the process call. + * + * @param e The `OnProgressEvent` that was emitted + */ + onProgress?: (e: OnProgressEvent) => void; + /** + * Emitted when the `OfflineContentOptions` is available after a `OfflineContentManager.getOptions` call. + * + * @param e The `OnOptionsAvailableEvent` that was emitted + */ + onOptionsAvailable?: (e: OnOptionsAvailableEvent) => void; + /** + * Emitted when the DRM license was updated. + * + * @param e The `OnDrmLicenseUpdatedEvent` that was emitted + */ + onDrmLicenseUpdated?: (e: OnDrmLicenseUpdatedEvent) => void; + /** + * Emitted when the DRM license has expired. + * + * @param e The `OnDrmLicenseExpiredEvent` that was emitted + */ + onDrmLicenseExpired?: (e: OnDrmLicenseExpiredEvent) => void; + /** + * Emitted when all active actions have been suspended. + * + * @param e The `OnSuspendedEvent` that was emitted + */ + onSuspended?: (e: OnSuspendedEvent) => void; + /** + * Emitted when all actions have been resumed. + * + * @param e The `OnResumedEvent` that was emitted + */ + onResumed?: (e: OnResumedEvent) => void; + /** + * Emitted when the download of the media content was canceled by the user and all partially downloaded content has been deleted from disk. + * + * @param e The `OnCanceledEvent` that was emitted + */ + onCanceled?: (e: OnCanceledEvent) => void; +} +//# sourceMappingURL=offlineContentManagerListener.d.ts.map \ No newline at end of file diff --git a/build/offline/offlineContentManagerListener.d.ts.map b/build/offline/offlineContentManagerListener.d.ts.map new file mode 100644 index 00000000..23430870 --- /dev/null +++ b/build/offline/offlineContentManagerListener.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineContentManagerListener.d.ts","sourceRoot":"","sources":["../../src/offline/offlineContentManagerListener.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/build/offline/offlineContentManagerListener.js b/build/offline/offlineContentManagerListener.js new file mode 100644 index 00000000..dddc02f7 --- /dev/null +++ b/build/offline/offlineContentManagerListener.js @@ -0,0 +1,17 @@ +/** + * Enum to hold the `eventType` on the `BitmovinNativeOfflineEventData` + * @remarks Platform: Android, iOS + */ +export var OfflineEventType; +(function (OfflineEventType) { + OfflineEventType["onCompleted"] = "onCompleted"; + OfflineEventType["onError"] = "onError"; + OfflineEventType["onProgress"] = "onProgress"; + OfflineEventType["onOptionsAvailable"] = "onOptionsAvailable"; + OfflineEventType["onDrmLicenseUpdated"] = "onDrmLicenseUpdated"; + OfflineEventType["onDrmLicenseExpired"] = "onDrmLicenseExpired"; + OfflineEventType["onSuspended"] = "onSuspended"; + OfflineEventType["onResumed"] = "onResumed"; + OfflineEventType["onCanceled"] = "onCanceled"; +})(OfflineEventType || (OfflineEventType = {})); +//# sourceMappingURL=offlineContentManagerListener.js.map \ No newline at end of file diff --git a/build/offline/offlineContentManagerListener.js.map b/build/offline/offlineContentManagerListener.js.map new file mode 100644 index 00000000..85d64569 --- /dev/null +++ b/build/offline/offlineContentManagerListener.js.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineContentManagerListener.js","sourceRoot":"","sources":["../../src/offline/offlineContentManagerListener.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,CAAN,IAAY,gBAUX;AAVD,WAAY,gBAAgB;IAC1B,+CAA2B,CAAA;IAC3B,uCAAmB,CAAA;IACnB,6CAAyB,CAAA;IACzB,6DAAyC,CAAA;IACzC,+DAA2C,CAAA;IAC3C,+DAA2C,CAAA;IAC3C,+CAA2B,CAAA;IAC3B,2CAAuB,CAAA;IACvB,6CAAyB,CAAA;AAC3B,CAAC,EAVW,gBAAgB,KAAhB,gBAAgB,QAU3B","sourcesContent":["import { OfflineContentOptions } from './offlineContentOptions';\nimport { OfflineState } from './offlineState';\n\n/**\n * Enum to hold the `eventType` on the `BitmovinNativeOfflineEventData`\n * @remarks Platform: Android, iOS\n */\nexport enum OfflineEventType {\n onCompleted = 'onCompleted',\n onError = 'onError',\n onProgress = 'onProgress',\n onOptionsAvailable = 'onOptionsAvailable',\n onDrmLicenseUpdated = 'onDrmLicenseUpdated',\n onDrmLicenseExpired = 'onDrmLicenseExpired',\n onSuspended = 'onSuspended',\n onResumed = 'onResumed',\n onCanceled = 'onCanceled',\n}\n\n/**\n * The base interface for all offline events.\n * @remarks Platform: Android, iOS\n */\nexport interface OfflineEvent {\n /**\n * The native id associated with the `OfflineContentManager` emitting this event\n */\n nativeId: string;\n /**\n * The supplied id representing the source associated with the `OfflineContentManager` emitting this event.\n */\n identifier: string;\n /**\n * The `OfflineEventType` that correlates to which native `OfflineContentManagerListener` method was called.\n */\n eventType: T;\n /**\n * The current offline download state\n */\n state: OfflineState;\n}\n\n/**\n * Emitted when the download process has completed.\n * @remarks Platform: Android, iOS\n */\nexport interface OnCompletedEvent\n extends OfflineEvent {\n /**\n * The options that are available to download\n */\n options?: OfflineContentOptions;\n}\n\n/**\n * Emitted when an error has occurred.\n * @remarks Platform: Android, iOS\n */\nexport interface OnErrorEvent extends OfflineEvent {\n /**\n * The error code of the process error\n */\n code?: number;\n /**\n * The error message of the process error\n */\n message?: string;\n}\n\n/**\n * Emitted when there is a progress change for the process call.\n * @remarks Platform: Android, iOS\n */\nexport interface OnProgressEvent\n extends OfflineEvent {\n /**\n * The progress for the current process\n */\n progress: number;\n}\n\n/**\n * Emitted when the `OfflineContentOptions` is available after a `OfflineContentManager.getOptions` call.\n * @remarks Platform: Android, iOS\n */\nexport interface OnOptionsAvailableEvent\n extends OfflineEvent {\n /**\n * The options that are available to download\n */\n options?: OfflineContentOptions;\n}\n\n/**\n * Emitted when the DRM license was updated.\n * @remarks Platform: Android, iOS\n */\nexport type OnDrmLicenseUpdatedEvent =\n OfflineEvent;\n\n/**\n * Emitted when the DRM license has expired.\n * @remarks Platform: iOS\n */\nexport type OnDrmLicenseExpiredEvent =\n OfflineEvent;\n\n/**\n * Emitted when all active actions have been suspended.\n * @remarks Platform: Android, iOS\n */\nexport type OnSuspendedEvent = OfflineEvent;\n\n/**\n * Emitted when all actions have been resumed.\n * @remarks Platform: Android, iOS\n */\nexport type OnResumedEvent = OfflineEvent;\n\n/**\n * Emitted when the download of the media content was canceled by the user and all partially downloaded content has been deleted from disk.\n * @remarks Platform: Android, iOS\n */\nexport type OnCanceledEvent = OfflineEvent;\n\n/**\n * The type aggregation for all possible native offline events received from the `DeviceEventEmitter`\n * @remarks Platform: Android, iOS\n */\nexport type BitmovinNativeOfflineEventData =\n | OnCompletedEvent\n | OnOptionsAvailableEvent\n | OnProgressEvent\n | OnErrorEvent\n | OnDrmLicenseUpdatedEvent\n | OnDrmLicenseExpiredEvent\n | OnSuspendedEvent\n | OnResumedEvent\n | OnCanceledEvent;\n\n/**\n * The listener that can be passed to the `OfflineContentManager` to receive callbacks for different events.\n * @remarks Platform: Android, iOS\n */\nexport interface OfflineContentManagerListener {\n /**\n * Emitted when the download process has completed.\n *\n * @param e The `OnCompletedEvent` that was emitted\n */\n onCompleted?: (e: OnCompletedEvent) => void;\n /**\n * Emitted when an error has occurred.\n *\n * @param e The `OnErrorEvent` that was emitted\n */\n onError?: (e: OnErrorEvent) => void;\n /**\n * Emitted when there is a progress change for the process call.\n *\n * @param e The `OnProgressEvent` that was emitted\n */\n onProgress?: (e: OnProgressEvent) => void;\n /**\n * Emitted when the `OfflineContentOptions` is available after a `OfflineContentManager.getOptions` call.\n *\n * @param e The `OnOptionsAvailableEvent` that was emitted\n */\n onOptionsAvailable?: (e: OnOptionsAvailableEvent) => void;\n /**\n * Emitted when the DRM license was updated.\n *\n * @param e The `OnDrmLicenseUpdatedEvent` that was emitted\n */\n onDrmLicenseUpdated?: (e: OnDrmLicenseUpdatedEvent) => void;\n /**\n * Emitted when the DRM license has expired.\n *\n * @param e The `OnDrmLicenseExpiredEvent` that was emitted\n */\n onDrmLicenseExpired?: (e: OnDrmLicenseExpiredEvent) => void;\n /**\n * Emitted when all active actions have been suspended.\n *\n * @param e The `OnSuspendedEvent` that was emitted\n */\n onSuspended?: (e: OnSuspendedEvent) => void;\n /**\n * Emitted when all actions have been resumed.\n *\n * @param e The `OnResumedEvent` that was emitted\n */\n onResumed?: (e: OnResumedEvent) => void;\n /**\n * Emitted when the download of the media content was canceled by the user and all partially downloaded content has been deleted from disk.\n *\n * @param e The `OnCanceledEvent` that was emitted\n */\n onCanceled?: (e: OnCanceledEvent) => void;\n}\n"]} \ No newline at end of file diff --git a/build/offline/offlineContentOptions.d.ts b/build/offline/offlineContentOptions.d.ts new file mode 100644 index 00000000..af991fc0 --- /dev/null +++ b/build/offline/offlineContentOptions.d.ts @@ -0,0 +1,29 @@ +/** + * Superclass of entries which can be selected to download for offline playback + * @remarks Platform: Android, iOS + */ +export interface OfflineContentOptionEntry { + /** + * The ID of the option. + */ + id: string; + /** + * The language of the option. + */ + language?: string; +} +/** + * Represents the downloadable options provided via the `onOptionsAvailable` callback on `OfflineContentManagerListener` + * @remarks Platform: Android, iOS + */ +export interface OfflineContentOptions { + /** + * Represents the audio options available for download + */ + audioOptions: OfflineContentOptionEntry[]; + /** + * Represents the text options available for download + */ + textOptions: OfflineContentOptionEntry[]; +} +//# sourceMappingURL=offlineContentOptions.d.ts.map \ No newline at end of file diff --git a/build/offline/offlineContentOptions.d.ts.map b/build/offline/offlineContentOptions.d.ts.map new file mode 100644 index 00000000..d3d6e6e6 --- /dev/null +++ b/build/offline/offlineContentOptions.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineContentOptions.d.ts","sourceRoot":"","sources":["../../src/offline/offlineContentOptions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,yBAAyB;IACxC;;OAEG;IACH,EAAE,EAAE,MAAM,CAAC;IACX;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,YAAY,EAAE,yBAAyB,EAAE,CAAC;IAC1C;;OAEG;IACH,WAAW,EAAE,yBAAyB,EAAE,CAAC;CAC1C"} \ No newline at end of file diff --git a/build/offline/offlineContentOptions.js b/build/offline/offlineContentOptions.js new file mode 100644 index 00000000..faa8efdd --- /dev/null +++ b/build/offline/offlineContentOptions.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=offlineContentOptions.js.map \ No newline at end of file diff --git a/build/offline/offlineContentOptions.js.map b/build/offline/offlineContentOptions.js.map new file mode 100644 index 00000000..4c7f9395 --- /dev/null +++ b/build/offline/offlineContentOptions.js.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineContentOptions.js","sourceRoot":"","sources":["../../src/offline/offlineContentOptions.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Superclass of entries which can be selected to download for offline playback\n * @remarks Platform: Android, iOS\n */\nexport interface OfflineContentOptionEntry {\n /**\n * The ID of the option.\n */\n id: string;\n /**\n * The language of the option.\n */\n language?: string;\n}\n\n/**\n * Represents the downloadable options provided via the `onOptionsAvailable` callback on `OfflineContentManagerListener`\n * @remarks Platform: Android, iOS\n */\nexport interface OfflineContentOptions {\n /**\n * Represents the audio options available for download\n */\n audioOptions: OfflineContentOptionEntry[];\n /**\n * Represents the text options available for download\n */\n textOptions: OfflineContentOptionEntry[];\n}\n"]} \ No newline at end of file diff --git a/build/offline/offlineDownloadRequest.d.ts b/build/offline/offlineDownloadRequest.d.ts new file mode 100644 index 00000000..98d5e005 --- /dev/null +++ b/build/offline/offlineDownloadRequest.d.ts @@ -0,0 +1,19 @@ +/** + * Represents the configuration to start a download. + * @remarks Platform: Android, iOS + */ +export interface OfflineDownloadRequest { + /** + * Minimum video bitrate to download. The nearest higher available bitrate will be selected. + */ + minimumBitrate?: number; + /** + * Audio tracks with IDs to download. + */ + audioOptionIds?: string[]; + /** + * Text tracks with IDs to download. + */ + textOptionIds?: string[]; +} +//# sourceMappingURL=offlineDownloadRequest.d.ts.map \ No newline at end of file diff --git a/build/offline/offlineDownloadRequest.d.ts.map b/build/offline/offlineDownloadRequest.d.ts.map new file mode 100644 index 00000000..c56d2f2a --- /dev/null +++ b/build/offline/offlineDownloadRequest.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineDownloadRequest.d.ts","sourceRoot":"","sources":["../../src/offline/offlineDownloadRequest.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAE1B;;OAEG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B"} \ No newline at end of file diff --git a/build/offline/offlineDownloadRequest.js b/build/offline/offlineDownloadRequest.js new file mode 100644 index 00000000..c3c03187 --- /dev/null +++ b/build/offline/offlineDownloadRequest.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=offlineDownloadRequest.js.map \ No newline at end of file diff --git a/build/offline/offlineDownloadRequest.js.map b/build/offline/offlineDownloadRequest.js.map new file mode 100644 index 00000000..d2152ed5 --- /dev/null +++ b/build/offline/offlineDownloadRequest.js.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineDownloadRequest.js","sourceRoot":"","sources":["../../src/offline/offlineDownloadRequest.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Represents the configuration to start a download.\n * @remarks Platform: Android, iOS\n */\nexport interface OfflineDownloadRequest {\n /**\n * Minimum video bitrate to download. The nearest higher available bitrate will be selected.\n */\n minimumBitrate?: number;\n\n /**\n * Audio tracks with IDs to download.\n */\n audioOptionIds?: string[];\n\n /**\n * Text tracks with IDs to download.\n */\n textOptionIds?: string[];\n}\n"]} \ No newline at end of file diff --git a/build/offline/offlineModule.d.ts b/build/offline/offlineModule.d.ts new file mode 100644 index 00000000..d19f7219 --- /dev/null +++ b/build/offline/offlineModule.d.ts @@ -0,0 +1,32 @@ +import { NativeModule } from 'expo-modules-core'; +import { SourceConfig } from '../source'; +import { OfflineDownloadRequest } from './offlineDownloadRequest'; +import { BitmovinNativeOfflineEventData } from './offlineContentManagerListener'; +export type OfflineModuleEvents = { + onBitmovinOfflineEvent: (event: BitmovinNativeOfflineEventData) => void; +}; +/** + * Native OfflineModule using Expo modules API. + * Provides modern async/await interface while maintaining backward compatibility. + */ +declare class OfflineModule extends NativeModule { + initializeWithConfig(nativeId: string, config: { + identifier: string; + sourceConfig: SourceConfig; + }, drmNativeId: string | undefined): Promise; + getState(nativeId: string): Promise; + getOptions(nativeId: string): Promise; + download(nativeId: string, request: OfflineDownloadRequest): Promise; + resume(nativeId: string): Promise; + suspend(nativeId: string): Promise; + cancelDownload(nativeId: string): Promise; + usedStorage(nativeId: string): Promise; + deleteAll(nativeId: string): Promise; + downloadLicense(nativeId: string): Promise; + releaseLicense(nativeId: string): Promise; + renewOfflineLicense(nativeId: string): Promise; + release(nativeId: string): Promise; +} +declare const _default: OfflineModule; +export default _default; +//# sourceMappingURL=offlineModule.d.ts.map \ No newline at end of file diff --git a/build/offline/offlineModule.d.ts.map b/build/offline/offlineModule.d.ts.map new file mode 100644 index 00000000..741cb5a2 --- /dev/null +++ b/build/offline/offlineModule.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineModule.d.ts","sourceRoot":"","sources":["../../src/offline/offlineModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,8BAA8B,EAAE,MAAM,iCAAiC,CAAC;AAEjF,MAAM,MAAM,mBAAmB,GAAG;IAChC,sBAAsB,EAAE,CAAC,KAAK,EAAE,8BAA8B,KAAK,IAAI,CAAC;CACzE,CAAC;AAEF;;;GAGG;AACH,OAAO,OAAO,aAAc,SAAQ,YAAY,CAAC,mBAAmB,CAAC;IACnE,oBAAoB,CAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,YAAY,CAAA;KAAE,EAC1D,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,OAAO,CAAC,IAAI,CAAC;IAEhB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAE3C,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAE3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAE1E,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEvC,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAExC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/C,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAE9C,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAE1C,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEhD,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAE/C,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEpD,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CACzC;;AAED,wBAAmE"} \ No newline at end of file diff --git a/build/offline/offlineModule.js b/build/offline/offlineModule.js new file mode 100644 index 00000000..368fbb7d --- /dev/null +++ b/build/offline/offlineModule.js @@ -0,0 +1,3 @@ +import { requireNativeModule } from 'expo-modules-core'; +export default requireNativeModule('OfflineModule'); +//# sourceMappingURL=offlineModule.js.map \ No newline at end of file diff --git a/build/offline/offlineModule.js.map b/build/offline/offlineModule.js.map new file mode 100644 index 00000000..cc81c76d --- /dev/null +++ b/build/offline/offlineModule.js.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineModule.js","sourceRoot":"","sources":["../../src/offline/offlineModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AA6CtE,eAAe,mBAAmB,CAAgB,eAAe,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo-modules-core';\nimport { SourceConfig } from '../source';\nimport { OfflineDownloadRequest } from './offlineDownloadRequest';\nimport { BitmovinNativeOfflineEventData } from './offlineContentManagerListener';\n\nexport type OfflineModuleEvents = {\n onBitmovinOfflineEvent: (event: BitmovinNativeOfflineEventData) => void;\n};\n\n/**\n * Native OfflineModule using Expo modules API.\n * Provides modern async/await interface while maintaining backward compatibility.\n */\ndeclare class OfflineModule extends NativeModule {\n initializeWithConfig(\n nativeId: string,\n config: { identifier: string; sourceConfig: SourceConfig },\n drmNativeId: string | undefined\n ): Promise;\n\n getState(nativeId: string): Promise;\n\n getOptions(nativeId: string): Promise;\n\n download(nativeId: string, request: OfflineDownloadRequest): Promise;\n\n resume(nativeId: string): Promise;\n\n suspend(nativeId: string): Promise;\n\n cancelDownload(nativeId: string): Promise;\n\n usedStorage(nativeId: string): Promise;\n\n deleteAll(nativeId: string): Promise;\n\n downloadLicense(nativeId: string): Promise;\n\n releaseLicense(nativeId: string): Promise;\n\n renewOfflineLicense(nativeId: string): Promise;\n\n release(nativeId: string): Promise;\n}\n\nexport default requireNativeModule('OfflineModule');\n"]} \ No newline at end of file diff --git a/build/offline/offlineSourceOptions.d.ts b/build/offline/offlineSourceOptions.d.ts new file mode 100644 index 00000000..62d79758 --- /dev/null +++ b/build/offline/offlineSourceOptions.d.ts @@ -0,0 +1,12 @@ +/** + * Object used configure how the native offline managers create and get offline source configurations + * @remarks Platform: Android, iOS + */ +export interface OfflineSourceOptions { + /** + * Whether or not the player should restrict playback only to audio, video and subtitle tracks which are stored offline on the device. This has to be set to true if the device has no network access. + * @remarks Platform: iOS + */ + restrictedToAssetCache?: boolean; +} +//# sourceMappingURL=offlineSourceOptions.d.ts.map \ No newline at end of file diff --git a/build/offline/offlineSourceOptions.d.ts.map b/build/offline/offlineSourceOptions.d.ts.map new file mode 100644 index 00000000..a445f4a4 --- /dev/null +++ b/build/offline/offlineSourceOptions.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineSourceOptions.d.ts","sourceRoot":"","sources":["../../src/offline/offlineSourceOptions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC"} \ No newline at end of file diff --git a/build/offline/offlineSourceOptions.js b/build/offline/offlineSourceOptions.js new file mode 100644 index 00000000..7194d864 --- /dev/null +++ b/build/offline/offlineSourceOptions.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=offlineSourceOptions.js.map \ No newline at end of file diff --git a/build/offline/offlineSourceOptions.js.map b/build/offline/offlineSourceOptions.js.map new file mode 100644 index 00000000..9b03d5fe --- /dev/null +++ b/build/offline/offlineSourceOptions.js.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineSourceOptions.js","sourceRoot":"","sources":["../../src/offline/offlineSourceOptions.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Object used configure how the native offline managers create and get offline source configurations\n * @remarks Platform: Android, iOS\n */\nexport interface OfflineSourceOptions {\n /**\n * Whether or not the player should restrict playback only to audio, video and subtitle tracks which are stored offline on the device. This has to be set to true if the device has no network access.\n * @remarks Platform: iOS\n */\n restrictedToAssetCache?: boolean;\n}\n"]} \ No newline at end of file diff --git a/build/offline/offlineState.d.ts b/build/offline/offlineState.d.ts new file mode 100644 index 00000000..08380934 --- /dev/null +++ b/build/offline/offlineState.d.ts @@ -0,0 +1,23 @@ +/** + * Contains the state an OfflineContentManager can have. + * @remarks Platform: Android, iOS + */ +export declare enum OfflineState { + /** + * The offline content is downloaded and ready for offline playback. + */ + Downloaded = "Downloaded", + /** + * The offline content is currently downloading. + */ + Downloading = "Downloading", + /** + * The download of the offline content is suspended, and is only partly downloaded yet. + */ + Suspended = "Suspended", + /** + * The offline content is not downloaded. However, some data may be still cached. + */ + NotDownloaded = "NotDownloaded" +} +//# sourceMappingURL=offlineState.d.ts.map \ No newline at end of file diff --git a/build/offline/offlineState.d.ts.map b/build/offline/offlineState.d.ts.map new file mode 100644 index 00000000..c0a0c6ef --- /dev/null +++ b/build/offline/offlineState.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineState.d.ts","sourceRoot":"","sources":["../../src/offline/offlineState.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,oBAAY,YAAY;IACtB;;OAEG;IACH,UAAU,eAAe;IACzB;;OAEG;IACH,WAAW,gBAAgB;IAC3B;;OAEG;IACH,SAAS,cAAc;IACvB;;OAEG;IACH,aAAa,kBAAkB;CAChC"} \ No newline at end of file diff --git a/build/offline/offlineState.js b/build/offline/offlineState.js new file mode 100644 index 00000000..dbbc7abb --- /dev/null +++ b/build/offline/offlineState.js @@ -0,0 +1,24 @@ +/** + * Contains the state an OfflineContentManager can have. + * @remarks Platform: Android, iOS + */ +export var OfflineState; +(function (OfflineState) { + /** + * The offline content is downloaded and ready for offline playback. + */ + OfflineState["Downloaded"] = "Downloaded"; + /** + * The offline content is currently downloading. + */ + OfflineState["Downloading"] = "Downloading"; + /** + * The download of the offline content is suspended, and is only partly downloaded yet. + */ + OfflineState["Suspended"] = "Suspended"; + /** + * The offline content is not downloaded. However, some data may be still cached. + */ + OfflineState["NotDownloaded"] = "NotDownloaded"; +})(OfflineState || (OfflineState = {})); +//# sourceMappingURL=offlineState.js.map \ No newline at end of file diff --git a/build/offline/offlineState.js.map b/build/offline/offlineState.js.map new file mode 100644 index 00000000..6c3a7201 --- /dev/null +++ b/build/offline/offlineState.js.map @@ -0,0 +1 @@ +{"version":3,"file":"offlineState.js","sourceRoot":"","sources":["../../src/offline/offlineState.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAN,IAAY,YAiBX;AAjBD,WAAY,YAAY;IACtB;;OAEG;IACH,yCAAyB,CAAA;IACzB;;OAEG;IACH,2CAA2B,CAAA;IAC3B;;OAEG;IACH,uCAAuB,CAAA;IACvB;;OAEG;IACH,+CAA+B,CAAA;AACjC,CAAC,EAjBW,YAAY,KAAZ,YAAY,QAiBvB","sourcesContent":["/**\n * Contains the state an OfflineContentManager can have.\n * @remarks Platform: Android, iOS\n */\nexport enum OfflineState {\n /**\n * The offline content is downloaded and ready for offline playback.\n */\n Downloaded = 'Downloaded',\n /**\n * The offline content is currently downloading.\n */\n Downloading = 'Downloading',\n /**\n * The download of the offline content is suspended, and is only partly downloaded yet.\n */\n Suspended = 'Suspended',\n /**\n * The offline content is not downloaded. However, some data may be still cached.\n */\n NotDownloaded = 'NotDownloaded',\n}\n"]} \ No newline at end of file diff --git a/build/playbackConfig.d.ts b/build/playbackConfig.d.ts new file mode 100644 index 00000000..753a2d3a --- /dev/null +++ b/build/playbackConfig.d.ts @@ -0,0 +1,88 @@ +import { DecoderConfig } from './decoder/decoderConfig'; +/** + * Configures the playback behaviour of the player. + */ +export interface PlaybackConfig { + /** + * Whether the player starts playing automatically after loading a source or not. Default is `false`. + * @example + * ``` + * const player = new Player({ + * playbackConfig: { + * isAutoplayEnabled: true, + * }, + * }); + * ``` + */ + isAutoplayEnabled?: boolean; + /** + * Whether the sound is muted on startup or not. Default value is `false`. + * @example + * ``` + * const player = new Player({ + * playbackConfig: { + * isMuted: true, + * }, + * }); + * ``` + */ + isMuted?: boolean; + /** + * Whether time shift / DVR for live streams is enabled or not. Default is `true`. + * @example + * ``` + * const player = new Player({ + * playbackConfig: { + * isTimeShiftEnabled: false, + * }, + * }); + * ``` + */ + isTimeShiftEnabled?: boolean; + /** + * Whether background playback is enabled or not. + * Default is `false`. + * + * When set to `true`, playback is not automatically paused + * anymore when the app moves to the background. + * When set to `true`, also make sure to properly configure your app to allow + * background playback. + * + * Default is `false`. + * + * @remarks + * - On Android, {@link MediaControlConfig.isEnabled} has to be `true` for + * background playback to work. + * - On tvOS, background playback is only supported for audio-only content. + * + * @example + * ``` + * const player = new Player({ + * playbackConfig: { + * isBackgroundPlaybackEnabled: true, + * }, + * }); + * ``` + */ + isBackgroundPlaybackEnabled?: boolean; + /** + * Whether the Picture in Picture mode option is enabled or not. Default is `false`. + * @example + * ``` + * const player = new Player({ + * playbackConfig: { + * isPictureInPictureEnabled: true, + * }, + * }); + * ``` + * @deprecated Use {@link PictureInPictureConfig.isEnabled} instead. + */ + isPictureInPictureEnabled?: boolean; + /** + * Configures decoder behaviour. + * + * @remarks Platform: Android + */ + decoderConfig?: DecoderConfig; +} +//# sourceMappingURL=playbackConfig.d.ts.map \ No newline at end of file diff --git a/build/playbackConfig.d.ts.map b/build/playbackConfig.d.ts.map new file mode 100644 index 00000000..3ca1d02d --- /dev/null +++ b/build/playbackConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"playbackConfig.d.ts","sourceRoot":"","sources":["../src/playbackConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;;;;OAUG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC;;;;;;;;;;;OAWG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;IAEpC;;;;OAIG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B"} \ No newline at end of file diff --git a/build/playbackConfig.js b/build/playbackConfig.js new file mode 100644 index 00000000..67e5423a --- /dev/null +++ b/build/playbackConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=playbackConfig.js.map \ No newline at end of file diff --git a/build/playbackConfig.js.map b/build/playbackConfig.js.map new file mode 100644 index 00000000..d502cd96 --- /dev/null +++ b/build/playbackConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"playbackConfig.js","sourceRoot":"","sources":["../src/playbackConfig.ts"],"names":[],"mappings":"","sourcesContent":["import { DecoderConfig } from './decoder/decoderConfig';\n\n/**\n * Configures the playback behaviour of the player.\n */\nexport interface PlaybackConfig {\n /**\n * Whether the player starts playing automatically after loading a source or not. Default is `false`.\n * @example\n * ```\n * const player = new Player({\n * playbackConfig: {\n * isAutoplayEnabled: true,\n * },\n * });\n * ```\n */\n isAutoplayEnabled?: boolean;\n /**\n * Whether the sound is muted on startup or not. Default value is `false`.\n * @example\n * ```\n * const player = new Player({\n * playbackConfig: {\n * isMuted: true,\n * },\n * });\n * ```\n */\n isMuted?: boolean;\n /**\n * Whether time shift / DVR for live streams is enabled or not. Default is `true`.\n * @example\n * ```\n * const player = new Player({\n * playbackConfig: {\n * isTimeShiftEnabled: false,\n * },\n * });\n * ```\n */\n isTimeShiftEnabled?: boolean;\n /**\n * Whether background playback is enabled or not.\n * Default is `false`.\n *\n * When set to `true`, playback is not automatically paused\n * anymore when the app moves to the background.\n * When set to `true`, also make sure to properly configure your app to allow\n * background playback.\n *\n * Default is `false`.\n *\n * @remarks\n * - On Android, {@link MediaControlConfig.isEnabled} has to be `true` for\n * background playback to work.\n * - On tvOS, background playback is only supported for audio-only content.\n *\n * @example\n * ```\n * const player = new Player({\n * playbackConfig: {\n * isBackgroundPlaybackEnabled: true,\n * },\n * });\n * ```\n */\n isBackgroundPlaybackEnabled?: boolean;\n /**\n * Whether the Picture in Picture mode option is enabled or not. Default is `false`.\n * @example\n * ```\n * const player = new Player({\n * playbackConfig: {\n * isPictureInPictureEnabled: true,\n * },\n * });\n * ```\n * @deprecated Use {@link PictureInPictureConfig.isEnabled} instead.\n */\n isPictureInPictureEnabled?: boolean;\n\n /**\n * Configures decoder behaviour.\n *\n * @remarks Platform: Android\n */\n decoderConfig?: DecoderConfig;\n}\n"]} \ No newline at end of file diff --git a/build/player.d.ts b/build/player.d.ts new file mode 100644 index 00000000..24a70713 --- /dev/null +++ b/build/player.d.ts @@ -0,0 +1,308 @@ +import NativeInstance from './nativeInstance'; +import { Source, SourceConfig } from './source'; +import { AudioTrack } from './audioTrack'; +import { SubtitleTrack } from './subtitleTrack'; +import { OfflineContentManager, OfflineSourceOptions } from './offline'; +import { Thumbnail } from './thumbnail'; +import { AnalyticsApi } from './analytics/player'; +import { PlayerConfig } from './playerConfig'; +import { AdItem } from './advertising'; +import { BufferApi } from './bufferApi'; +import { VideoQuality } from './media'; +/** + * Loads, controls and renders audio and video content represented through {@link Source}s. A player + * instance can be created via the {@link usePlayer} hook and will idle until one or more {@link Source}s are + * loaded. Once {@link Player.load} or {@link Player.loadSource} is called, the player becomes active and initiates necessary downloads to + * start playback of the loaded source(s). + * + * Can be attached to {@link PlayerView} component in order to use Bitmovin's Player Web UI. + * @see PlayerView + */ +export declare class Player extends NativeInstance { + /** + * Whether the native `Player` object has been created. + */ + isInitialized: boolean; + /** + * Whether the native `Player` object has been disposed. + */ + isDestroyed: boolean; + /** + * Currently active source, or `null` if none is active. + */ + source?: Source; + /** + * The `AnalyticsApi` for interactions regarding the `Player`'s analytics. + * + * `undefined` if the player was created without analytics support. + */ + analytics?: AnalyticsApi; + /** + * The {@link BufferApi} for interactions regarding the buffer. + */ + buffer: BufferApi; + private network?; + private decoderConfig?; + /** + * Allocates the native `Player` instance and its resources natively. + */ + initialize: () => Promise; + /** + * Destroys the native `Player` and releases all of its allocated resources. + */ + destroy: () => Promise; + /** + * Loads a new {@link Source} from `sourceConfig` into the player. + */ + load: (sourceConfig: SourceConfig) => Promise | void; + /** + * Loads the downloaded content from {@link OfflineContentManager} into the player. + */ + loadOfflineContent: (offlineContentManager: OfflineContentManager, options?: OfflineSourceOptions) => Promise | void; + /** + * Loads the given {@link Source} into the player. + */ + loadSource: (source: Source) => Promise; + /** + * Unloads all {@link Source}s from the player. + */ + unload: () => Promise | void; + /** + * Starts or resumes playback after being paused. Has no effect if the player is already playing. + */ + play: () => Promise | void; + /** + * Pauses the video if it is playing. Has no effect if the player is already paused. + */ + pause: () => Promise | void; + /** + * Seeks to the given playback time specified by the parameter `time` in seconds. Must not be + * greater than the total duration of the video. Has no effect when watching a live stream since + * seeking is not possible. + * + * @param time - The time to seek to in seconds. + */ + seek: (time: number) => Promise | void; + /** + * Shifts the time to the given `offset` in seconds from the live edge. The resulting offset has to be within the + * timeShift window as specified by `maxTimeShift` (which is a negative value) and 0. When the provided `offset` is + * positive, it will be interpreted as a UNIX timestamp in seconds and converted to fit into the timeShift window. + * When the provided `offset` is negative, but lower than `maxTimeShift`, then it will be clamped to `maxTimeShift`. + * Has no effect for VoD. + * + * Has no effect if no sources are loaded. + * + * @param offset - Target offset from the live edge in seconds. + */ + timeShift: (offset: number) => Promise | void; + /** + * Mutes the player if an audio track is available. Has no effect if the player is already muted. + */ + mute: () => Promise | void; + /** + * Unmutes the player if it is muted. Has no effect if the player is already unmuted. + */ + unmute: () => Promise | void; + /** + * Sets the player's volume between 0 (silent) and 100 (max volume). + * + * @param volume - The volume level to set. + */ + setVolume: (volume: number) => Promise | void; + /** + * @returns The player's current volume level. + */ + getVolume: () => Promise; + /** + * @returns The current playback time in seconds. + * + * For VoD streams the returned time ranges between 0 and the duration of the asset. + * + * For live streams it can be specified if an absolute UNIX timestamp or a value + * relative to the playback start should be returned. + * + * @param mode - The time mode to specify: an absolute UNIX timestamp ('absolute') or relative time ('relative'). + */ + getCurrentTime: (mode?: "relative" | "absolute") => Promise; + /** + * @returns The total duration in seconds of the current video or INFINITY if it’s a live stream. + */ + getDuration: () => Promise; + /** + * @returns `true` if the player is muted. + */ + isMuted: () => Promise; + /** + * @returns `true` if the player is currently playing, i.e. has started and is not paused. + */ + isPlaying: () => Promise; + /** + * @returns `true` if the player has started playback but it's currently paused. + */ + isPaused: () => Promise; + /** + * @returns `true` if the displayed video is a live stream. + */ + isLive: () => Promise; + /** + * @remarks Only available for iOS devices. + * @returns `true` when media is played externally using AirPlay. + */ + isAirPlayActive: () => Promise; + /** + * @remarks Only available for iOS devices. + * @returns `true` when AirPlay is available. + */ + isAirPlayAvailable: () => Promise; + /** + * @returns The currently selected audio track or `null`. + */ + getAudioTrack: () => Promise; + /** + * @returns An array containing {@link AudioTrack} objects for all available audio tracks. + */ + getAvailableAudioTracks: () => Promise; + /** + * Sets the audio track to the ID specified by trackIdentifier. A list can be retrieved by calling getAvailableAudioTracks. + * + * @param trackIdentifier - The {@link AudioTrack.identifier} to be set. + */ + setAudioTrack: (trackIdentifier: string) => Promise; + /** + * @returns The currently selected {@link SubtitleTrack} or `null`. + */ + getSubtitleTrack: () => Promise; + /** + * @returns An array containing SubtitleTrack objects for all available subtitle tracks. + */ + getAvailableSubtitles: () => Promise; + /** + * Sets the subtitle track to the ID specified by trackIdentifier. A list can be retrieved by calling getAvailableSubtitles. + * + * @param trackIdentifier - The {@link SubtitleTrack.identifier} to be set. + */ + setSubtitleTrack: (trackIdentifier?: string) => Promise; + /** + * Dynamically schedules the {@link AdItem} for playback. + * Has no effect if there is no active playback session. + * + * @param adItem - Ad to be scheduled for playback. + * + * @remarks Platform: iOS, Android + */ + scheduleAd: (adItem: AdItem) => Promise | void; + /** + * Skips the current ad. + * Has no effect if the current ad is not skippable or if no ad is being played back. + * + * @remarks Platform: iOS, Android + */ + skipAd: () => Promise | void; + /** + * @returns `true` while an ad is being played back or when main content playback has been paused for ad playback. + * @remarks Platform: iOS, Android + */ + isAd: () => Promise; + /** + * The current time shift of the live stream in seconds. This value is always 0 if the active {@link Source} is not a + * live stream or no sources are loaded. + */ + getTimeShift: () => Promise; + /** + * The limit in seconds for time shifting. This value is either negative or 0 and it is always 0 if the active + * {@link Source} is not a live stream or no sources are loaded. + */ + getMaxTimeShift: () => Promise; + /** + * Sets the upper bitrate boundary for video qualities. All qualities with a bitrate + * that is higher than this threshold will not be eligible for automatic quality selection. + * + * Can be set to `null` for no limitation. + */ + setMaxSelectableBitrate: (bitrate: number | null) => Promise | void; + /** + * @returns a {@link Thumbnail} for the specified playback time for the currently active source if available. + * Supported thumbnail formats are: + * - `WebVtt` configured via {@link SourceConfig.thumbnailTrack}, on all supported platforms + * - HLS `Image Media Playlist` in the multivariant playlist, Android-only + * - DASH `Image Adaptation Set` as specified in DASH-IF IOP, Android-only + * If a `WebVtt` thumbnail track is provided, any potential in-manifest thumbnails are ignored on Android. + * + * @param time - The time in seconds for which to retrieve the thumbnail. + */ + getThumbnail: (time: number) => Promise; + /** + * Whether casting to a cast-compatible remote device is available. {@link CastAvailableEvent} signals when + * casting becomes available. + * + * @remarks Platform: iOS, Android + */ + isCastAvailable: () => Promise; + /** + * Whether video is currently being casted to a remote device and not played locally. + * + * @remarks Platform: iOS, Android + */ + isCasting: () => Promise; + /** + * Initiates casting the current video to a cast-compatible remote device. The user has to choose to which device it + * should be sent. + * + * @remarks Platform: iOS, Android + */ + castVideo: () => Promise | void; + /** + * Stops casting the current video. Has no effect if {@link Player.isCasting} is `false`. + * + * @remarks Platform: iOS, Android + */ + castStop: () => Promise | void; + /** + * Returns the currently selected video quality. + * @returns The currently selected video quality. + */ + getVideoQuality: () => Promise; + /** + * Returns an array containing all available video qualities the player can adapt between. + * @returns An array containing all available video qualities the player can adapt between. + */ + getAvailableVideoQualities: () => Promise; + /** + * Sets the video quality. + * @remarks Platform: Android + * + * @param qualityId value obtained from {@link VideoQuality}'s `id` property, which can be obtained via `Player.getAvailableVideoQualities()` to select a specific quality. To use automatic quality selection, 'auto' can be passed here. + */ + setVideoQuality: (qualityId: string) => Promise | void; + /** + * Sets the playback speed of the player. Fast forward, slow motion and reverse playback are supported. + * @remarks + * Platform: iOS, tvOS + * + * - Slow motion is indicated by values between `0` and `1`. + * - Fast forward by values greater than `1`. + * - Slow reverse is used by values between `0` and `-1`, and fast reverse is used by values less than `-1`. iOS and tvOS only. + * - Negative values are ignored during Casting and on Android. + * - During reverse playback the playback will continue until the beginning of the active source is + * reached. When reaching the beginning of the source, playback will be paused and the playback + * speed will be reset to its default value of `1`. No {@link PlaybackFinishedEvent} will be + * emitted in this case. + * + * @param playbackSpeed - The playback speed to set. + */ + setPlaybackSpeed: (playbackSpeed: number) => Promise | void; + /** + * @see {@link setPlaybackSpeed} for details on which values playback speed can assume. + * @returns The player's current playback speed. + */ + getPlaybackSpeed: () => Promise; + /** + * Checks the possibility to play the media at specified playback speed. + * @param playbackSpeed - The playback speed to check. + * @returns `true` if it's possible to play the media at the specified playback speed, otherwise `false`. On Android it always returns `undefined`. + * @remarks Platform: iOS, tvOS + */ + canPlayAtPlaybackSpeed: (playbackSpeed: number) => Promise; + private maybeInitDecoderConfig; +} +//# sourceMappingURL=player.d.ts.map \ No newline at end of file diff --git a/build/player.d.ts.map b/build/player.d.ts.map new file mode 100644 index 00000000..015cfa76 --- /dev/null +++ b/build/player.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"player.d.ts","sourceRoot":"","sources":["../src/player.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/build/player.js b/build/player.js new file mode 100644 index 00000000..5240750c --- /dev/null +++ b/build/player.js @@ -0,0 +1,445 @@ +import { Platform } from 'react-native'; +import PlayerModule from './modules/PlayerModule'; +import NativeInstance from './nativeInstance'; +import { Source } from './source'; +import { AnalyticsApi } from './analytics/player'; +import { BufferApi } from './bufferApi'; +import { Network } from './network'; +import { DecoderConfigBridge } from './decoder'; +/** + * Loads, controls and renders audio and video content represented through {@link Source}s. A player + * instance can be created via the {@link usePlayer} hook and will idle until one or more {@link Source}s are + * loaded. Once {@link Player.load} or {@link Player.loadSource} is called, the player becomes active and initiates necessary downloads to + * start playback of the loaded source(s). + * + * Can be attached to {@link PlayerView} component in order to use Bitmovin's Player Web UI. + * @see PlayerView + */ +export class Player extends NativeInstance { + /** + * Whether the native `Player` object has been created. + */ + isInitialized = false; + /** + * Whether the native `Player` object has been disposed. + */ + isDestroyed = false; + /** + * Currently active source, or `null` if none is active. + */ + source; + /** + * The `AnalyticsApi` for interactions regarding the `Player`'s analytics. + * + * `undefined` if the player was created without analytics support. + */ + analytics = undefined; + /** + * The {@link BufferApi} for interactions regarding the buffer. + */ + buffer = new BufferApi(this.nativeId); + network; + decoderConfig; + /** + * Allocates the native `Player` instance and its resources natively. + */ + initialize = async () => { + if (!this.isInitialized) { + if (this.config?.networkConfig) { + this.network = new Network(this.config.networkConfig); + await this.network.initialize(); + } + await this.maybeInitDecoderConfig(); + const analyticsConfig = this.config?.analyticsConfig; + if (analyticsConfig) { + await PlayerModule.initializeWithAnalyticsConfig(this.nativeId, analyticsConfig, this.config, this.network?.nativeId, this.decoderConfig?.nativeId); + this.analytics = new AnalyticsApi(this.nativeId); + } + else { + await PlayerModule.initializeWithConfig(this.nativeId, this.config, this.network?.nativeId, this.decoderConfig?.nativeId); + } + this.isInitialized = true; + } + return Promise.resolve(); + }; + /** + * Destroys the native `Player` and releases all of its allocated resources. + */ + destroy = async () => { + if (!this.isDestroyed) { + await PlayerModule.destroy(this.nativeId); + this.source?.destroy(); + this.network?.destroy(); + this.decoderConfig?.destroy(); + this.isDestroyed = true; + } + return Promise.resolve(); + }; + /** + * Loads a new {@link Source} from `sourceConfig` into the player. + */ + load = (sourceConfig) => { + return this.loadSource(new Source(sourceConfig)); + }; + /** + * Loads the downloaded content from {@link OfflineContentManager} into the player. + */ + loadOfflineContent = (offlineContentManager, options) => { + return PlayerModule.loadOfflineContent(this.nativeId, offlineContentManager.nativeId, options); + }; + /** + * Loads the given {@link Source} into the player. + */ + loadSource = async (source) => { + this.source = source; + await source.initialize(); + return PlayerModule.loadSource(this.nativeId, source.nativeId); + }; + /** + * Unloads all {@link Source}s from the player. + */ + unload = () => { + return PlayerModule.unload(this.nativeId); + }; + /** + * Starts or resumes playback after being paused. Has no effect if the player is already playing. + */ + play = () => { + return PlayerModule.play(this.nativeId); + }; + /** + * Pauses the video if it is playing. Has no effect if the player is already paused. + */ + pause = () => { + return PlayerModule.pause(this.nativeId); + }; + /** + * Seeks to the given playback time specified by the parameter `time` in seconds. Must not be + * greater than the total duration of the video. Has no effect when watching a live stream since + * seeking is not possible. + * + * @param time - The time to seek to in seconds. + */ + seek = (time) => { + return PlayerModule.seek(this.nativeId, time); + }; + /** + * Shifts the time to the given `offset` in seconds from the live edge. The resulting offset has to be within the + * timeShift window as specified by `maxTimeShift` (which is a negative value) and 0. When the provided `offset` is + * positive, it will be interpreted as a UNIX timestamp in seconds and converted to fit into the timeShift window. + * When the provided `offset` is negative, but lower than `maxTimeShift`, then it will be clamped to `maxTimeShift`. + * Has no effect for VoD. + * + * Has no effect if no sources are loaded. + * + * @param offset - Target offset from the live edge in seconds. + */ + timeShift = (offset) => { + return PlayerModule.timeShift(this.nativeId, offset); + }; + /** + * Mutes the player if an audio track is available. Has no effect if the player is already muted. + */ + mute = () => { + return PlayerModule.mute(this.nativeId); + }; + /** + * Unmutes the player if it is muted. Has no effect if the player is already unmuted. + */ + unmute = () => { + return PlayerModule.unmute(this.nativeId); + }; + /** + * Sets the player's volume between 0 (silent) and 100 (max volume). + * + * @param volume - The volume level to set. + */ + setVolume = (volume) => { + return PlayerModule.setVolume(this.nativeId, volume); + }; + /** + * @returns The player's current volume level. + */ + getVolume = async () => { + return (await PlayerModule.getVolume(this.nativeId)) ?? 0; + }; + /** + * @returns The current playback time in seconds. + * + * For VoD streams the returned time ranges between 0 and the duration of the asset. + * + * For live streams it can be specified if an absolute UNIX timestamp or a value + * relative to the playback start should be returned. + * + * @param mode - The time mode to specify: an absolute UNIX timestamp ('absolute') or relative time ('relative'). + */ + getCurrentTime = async (mode = 'absolute') => { + return (await PlayerModule.currentTime(this.nativeId, mode)) ?? 0; + }; + /** + * @returns The total duration in seconds of the current video or INFINITY if it’s a live stream. + */ + getDuration = async () => { + return (await PlayerModule.duration(this.nativeId)) ?? 0; + }; + /** + * @returns `true` if the player is muted. + */ + isMuted = async () => { + return (await PlayerModule.isMuted(this.nativeId)) ?? false; + }; + /** + * @returns `true` if the player is currently playing, i.e. has started and is not paused. + */ + isPlaying = async () => { + return (await PlayerModule.isPlaying(this.nativeId)) ?? false; + }; + /** + * @returns `true` if the player has started playback but it's currently paused. + */ + isPaused = async () => { + return (await PlayerModule.isPaused(this.nativeId)) ?? false; + }; + /** + * @returns `true` if the displayed video is a live stream. + */ + isLive = async () => { + return (await PlayerModule.isLive(this.nativeId)) ?? false; + }; + /** + * @remarks Only available for iOS devices. + * @returns `true` when media is played externally using AirPlay. + */ + isAirPlayActive = async () => { + if (Platform.OS === 'android') { + console.warn(`[Player ${this.nativeId}] Method isAirPlayActive is not available for Android. Only iOS devices.`); + return false; + } + return (await PlayerModule.isAirPlayActive(this.nativeId)) ?? false; + }; + /** + * @remarks Only available for iOS devices. + * @returns `true` when AirPlay is available. + */ + isAirPlayAvailable = async () => { + if (Platform.OS === 'android') { + console.warn(`[Player ${this.nativeId}] Method isAirPlayAvailable is not available for Android. Only iOS devices.`); + return false; + } + return (await PlayerModule.isAirPlayAvailable(this.nativeId)) ?? false; + }; + /** + * @returns The currently selected audio track or `null`. + */ + getAudioTrack = async () => { + return PlayerModule.getAudioTrack(this.nativeId); + }; + /** + * @returns An array containing {@link AudioTrack} objects for all available audio tracks. + */ + getAvailableAudioTracks = async () => { + return PlayerModule.getAvailableAudioTracks(this.nativeId); + }; + /** + * Sets the audio track to the ID specified by trackIdentifier. A list can be retrieved by calling getAvailableAudioTracks. + * + * @param trackIdentifier - The {@link AudioTrack.identifier} to be set. + */ + setAudioTrack = async (trackIdentifier) => { + return PlayerModule.setAudioTrack(this.nativeId, trackIdentifier); + }; + /** + * @returns The currently selected {@link SubtitleTrack} or `null`. + */ + getSubtitleTrack = async () => { + return PlayerModule.getSubtitleTrack(this.nativeId); + }; + /** + * @returns An array containing SubtitleTrack objects for all available subtitle tracks. + */ + getAvailableSubtitles = async () => { + return PlayerModule.getAvailableSubtitles(this.nativeId); + }; + /** + * Sets the subtitle track to the ID specified by trackIdentifier. A list can be retrieved by calling getAvailableSubtitles. + * + * @param trackIdentifier - The {@link SubtitleTrack.identifier} to be set. + */ + setSubtitleTrack = async (trackIdentifier) => { + return PlayerModule.setSubtitleTrack(this.nativeId, trackIdentifier ?? ''); + }; + /** + * Dynamically schedules the {@link AdItem} for playback. + * Has no effect if there is no active playback session. + * + * @param adItem - Ad to be scheduled for playback. + * + * @remarks Platform: iOS, Android + */ + scheduleAd = (adItem) => { + return PlayerModule.scheduleAd(this.nativeId, adItem); + }; + /** + * Skips the current ad. + * Has no effect if the current ad is not skippable or if no ad is being played back. + * + * @remarks Platform: iOS, Android + */ + skipAd = () => { + return PlayerModule.skipAd(this.nativeId); + }; + /** + * @returns `true` while an ad is being played back or when main content playback has been paused for ad playback. + * @remarks Platform: iOS, Android + */ + isAd = async () => { + return (await PlayerModule.isAd(this.nativeId)) ?? false; + }; + /** + * The current time shift of the live stream in seconds. This value is always 0 if the active {@link Source} is not a + * live stream or no sources are loaded. + */ + getTimeShift = async () => { + return (await PlayerModule.getTimeShift(this.nativeId)) ?? 0; + }; + /** + * The limit in seconds for time shifting. This value is either negative or 0 and it is always 0 if the active + * {@link Source} is not a live stream or no sources are loaded. + */ + getMaxTimeShift = async () => { + return (await PlayerModule.getMaxTimeShift(this.nativeId)) ?? 0; + }; + /** + * Sets the upper bitrate boundary for video qualities. All qualities with a bitrate + * that is higher than this threshold will not be eligible for automatic quality selection. + * + * Can be set to `null` for no limitation. + */ + setMaxSelectableBitrate = (bitrate) => { + return PlayerModule.setMaxSelectableBitrate(this.nativeId, bitrate || -1); + }; + /** + * @returns a {@link Thumbnail} for the specified playback time for the currently active source if available. + * Supported thumbnail formats are: + * - `WebVtt` configured via {@link SourceConfig.thumbnailTrack}, on all supported platforms + * - HLS `Image Media Playlist` in the multivariant playlist, Android-only + * - DASH `Image Adaptation Set` as specified in DASH-IF IOP, Android-only + * If a `WebVtt` thumbnail track is provided, any potential in-manifest thumbnails are ignored on Android. + * + * @param time - The time in seconds for which to retrieve the thumbnail. + */ + getThumbnail = async (time) => { + return PlayerModule.getThumbnail(this.nativeId, time); + }; + /** + * Whether casting to a cast-compatible remote device is available. {@link CastAvailableEvent} signals when + * casting becomes available. + * + * @remarks Platform: iOS, Android + */ + isCastAvailable = async () => { + return (await PlayerModule.isCastAvailable(this.nativeId)) ?? false; + }; + /** + * Whether video is currently being casted to a remote device and not played locally. + * + * @remarks Platform: iOS, Android + */ + isCasting = async () => { + return (await PlayerModule.isCasting(this.nativeId)) ?? false; + }; + /** + * Initiates casting the current video to a cast-compatible remote device. The user has to choose to which device it + * should be sent. + * + * @remarks Platform: iOS, Android + */ + castVideo = () => { + return PlayerModule.castVideo(this.nativeId); + }; + /** + * Stops casting the current video. Has no effect if {@link Player.isCasting} is `false`. + * + * @remarks Platform: iOS, Android + */ + castStop = () => { + return PlayerModule.castStop(this.nativeId); + }; + /** + * Returns the currently selected video quality. + * @returns The currently selected video quality. + */ + getVideoQuality = async () => { + return PlayerModule.getVideoQuality(this.nativeId); + }; + /** + * Returns an array containing all available video qualities the player can adapt between. + * @returns An array containing all available video qualities the player can adapt between. + */ + getAvailableVideoQualities = async () => { + return PlayerModule.getAvailableVideoQualities(this.nativeId); + }; + /** + * Sets the video quality. + * @remarks Platform: Android + * + * @param qualityId value obtained from {@link VideoQuality}'s `id` property, which can be obtained via `Player.getAvailableVideoQualities()` to select a specific quality. To use automatic quality selection, 'auto' can be passed here. + */ + setVideoQuality = (qualityId) => { + if (Platform.OS !== 'android') { + console.warn(`[Player ${this.nativeId}] Method setVideoQuality is not available for iOS and tvOS devices. Only Android devices.`); + return Promise.resolve(); + } + return PlayerModule.setVideoQuality(this.nativeId, qualityId); + }; + /** + * Sets the playback speed of the player. Fast forward, slow motion and reverse playback are supported. + * @remarks + * Platform: iOS, tvOS + * + * - Slow motion is indicated by values between `0` and `1`. + * - Fast forward by values greater than `1`. + * - Slow reverse is used by values between `0` and `-1`, and fast reverse is used by values less than `-1`. iOS and tvOS only. + * - Negative values are ignored during Casting and on Android. + * - During reverse playback the playback will continue until the beginning of the active source is + * reached. When reaching the beginning of the source, playback will be paused and the playback + * speed will be reset to its default value of `1`. No {@link PlaybackFinishedEvent} will be + * emitted in this case. + * + * @param playbackSpeed - The playback speed to set. + */ + setPlaybackSpeed = (playbackSpeed) => { + return PlayerModule.setPlaybackSpeed(this.nativeId, playbackSpeed); + }; + /** + * @see {@link setPlaybackSpeed} for details on which values playback speed can assume. + * @returns The player's current playback speed. + */ + getPlaybackSpeed = async () => { + return (await PlayerModule.getPlaybackSpeed(this.nativeId)) ?? 0; + }; + /** + * Checks the possibility to play the media at specified playback speed. + * @param playbackSpeed - The playback speed to check. + * @returns `true` if it's possible to play the media at the specified playback speed, otherwise `false`. On Android it always returns `undefined`. + * @remarks Platform: iOS, tvOS + */ + canPlayAtPlaybackSpeed = async (playbackSpeed) => { + if (Platform.OS === 'android') { + console.warn(`[Player ${this.nativeId}] Method canPlayAtPlaybackSpeed is not available for Android. Only iOS and tvOS devices.`); + return undefined; + } + return ((await PlayerModule.canPlayAtPlaybackSpeed(this.nativeId, playbackSpeed)) ?? false); + }; + maybeInitDecoderConfig = () => { + if (this.config?.playbackConfig?.decoderConfig == null) { + return; + } + if (Platform.OS === 'ios') { + return; + } + this.decoderConfig = new DecoderConfigBridge(this.config.playbackConfig.decoderConfig); + this.decoderConfig.initialize(); + }; +} +//# sourceMappingURL=player.js.map \ No newline at end of file diff --git a/build/player.js.map b/build/player.js.map new file mode 100644 index 00000000..edd5787c --- /dev/null +++ b/build/player.js.map @@ -0,0 +1 @@ +{"version":3,"file":"player.js","sourceRoot":"","sources":["../src/player.ts"],"names":[],"mappings":"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","sourcesContent":["import { Platform } from 'react-native';\nimport PlayerModule from './modules/PlayerModule';\nimport NativeInstance from './nativeInstance';\nimport { Source, SourceConfig } from './source';\nimport { AudioTrack } from './audioTrack';\nimport { SubtitleTrack } from './subtitleTrack';\nimport { OfflineContentManager, OfflineSourceOptions } from './offline';\nimport { Thumbnail } from './thumbnail';\nimport { AnalyticsApi } from './analytics/player';\nimport { PlayerConfig } from './playerConfig';\nimport { AdItem } from './advertising';\nimport { BufferApi } from './bufferApi';\nimport { VideoQuality } from './media';\nimport { Network } from './network';\nimport { DecoderConfigBridge } from './decoder';\n\n/**\n * Loads, controls and renders audio and video content represented through {@link Source}s. A player\n * instance can be created via the {@link usePlayer} hook and will idle until one or more {@link Source}s are\n * loaded. Once {@link Player.load} or {@link Player.loadSource} is called, the player becomes active and initiates necessary downloads to\n * start playback of the loaded source(s).\n *\n * Can be attached to {@link PlayerView} component in order to use Bitmovin's Player Web UI.\n * @see PlayerView\n */\nexport class Player extends NativeInstance {\n /**\n * Whether the native `Player` object has been created.\n */\n isInitialized = false;\n /**\n * Whether the native `Player` object has been disposed.\n */\n isDestroyed = false;\n /**\n * Currently active source, or `null` if none is active.\n */\n source?: Source;\n /**\n * The `AnalyticsApi` for interactions regarding the `Player`'s analytics.\n *\n * `undefined` if the player was created without analytics support.\n */\n analytics?: AnalyticsApi = undefined;\n /**\n * The {@link BufferApi} for interactions regarding the buffer.\n */\n buffer: BufferApi = new BufferApi(this.nativeId);\n\n private network?: Network;\n\n private decoderConfig?: DecoderConfigBridge;\n /**\n * Allocates the native `Player` instance and its resources natively.\n */\n initialize = async (): Promise => {\n if (!this.isInitialized) {\n if (this.config?.networkConfig) {\n this.network = new Network(this.config.networkConfig);\n await this.network.initialize();\n }\n await this.maybeInitDecoderConfig();\n const analyticsConfig = this.config?.analyticsConfig;\n if (analyticsConfig) {\n await PlayerModule.initializeWithAnalyticsConfig(\n this.nativeId,\n analyticsConfig,\n this.config,\n this.network?.nativeId,\n this.decoderConfig?.nativeId\n );\n this.analytics = new AnalyticsApi(this.nativeId);\n } else {\n await PlayerModule.initializeWithConfig(\n this.nativeId,\n this.config,\n this.network?.nativeId,\n this.decoderConfig?.nativeId\n );\n }\n\n this.isInitialized = true;\n }\n return Promise.resolve();\n };\n\n /**\n * Destroys the native `Player` and releases all of its allocated resources.\n */\n destroy = async (): Promise => {\n if (!this.isDestroyed) {\n await PlayerModule.destroy(this.nativeId);\n this.source?.destroy();\n this.network?.destroy();\n this.decoderConfig?.destroy();\n this.isDestroyed = true;\n }\n\n return Promise.resolve();\n };\n\n /**\n * Loads a new {@link Source} from `sourceConfig` into the player.\n */\n load = (sourceConfig: SourceConfig): Promise | void => {\n return this.loadSource(new Source(sourceConfig));\n };\n\n /**\n * Loads the downloaded content from {@link OfflineContentManager} into the player.\n */\n loadOfflineContent = (\n offlineContentManager: OfflineContentManager,\n options?: OfflineSourceOptions\n ): Promise | void => {\n return PlayerModule.loadOfflineContent(\n this.nativeId,\n offlineContentManager.nativeId,\n options\n );\n };\n\n /**\n * Loads the given {@link Source} into the player.\n */\n loadSource = async (source: Source): Promise => {\n this.source = source;\n await source.initialize();\n return PlayerModule.loadSource(this.nativeId, source.nativeId);\n };\n\n /**\n * Unloads all {@link Source}s from the player.\n */\n unload = (): Promise | void => {\n return PlayerModule.unload(this.nativeId);\n };\n\n /**\n * Starts or resumes playback after being paused. Has no effect if the player is already playing.\n */\n play = (): Promise | void => {\n return PlayerModule.play(this.nativeId);\n };\n\n /**\n * Pauses the video if it is playing. Has no effect if the player is already paused.\n */\n pause = (): Promise | void => {\n return PlayerModule.pause(this.nativeId);\n };\n\n /**\n * Seeks to the given playback time specified by the parameter `time` in seconds. Must not be\n * greater than the total duration of the video. Has no effect when watching a live stream since\n * seeking is not possible.\n *\n * @param time - The time to seek to in seconds.\n */\n seek = (time: number): Promise | void => {\n return PlayerModule.seek(this.nativeId, time);\n };\n\n /**\n * Shifts the time to the given `offset` in seconds from the live edge. The resulting offset has to be within the\n * timeShift window as specified by `maxTimeShift` (which is a negative value) and 0. When the provided `offset` is\n * positive, it will be interpreted as a UNIX timestamp in seconds and converted to fit into the timeShift window.\n * When the provided `offset` is negative, but lower than `maxTimeShift`, then it will be clamped to `maxTimeShift`.\n * Has no effect for VoD.\n *\n * Has no effect if no sources are loaded.\n *\n * @param offset - Target offset from the live edge in seconds.\n */\n timeShift = (offset: number): Promise | void => {\n return PlayerModule.timeShift(this.nativeId, offset);\n };\n\n /**\n * Mutes the player if an audio track is available. Has no effect if the player is already muted.\n */\n mute = (): Promise | void => {\n return PlayerModule.mute(this.nativeId);\n };\n\n /**\n * Unmutes the player if it is muted. Has no effect if the player is already unmuted.\n */\n unmute = (): Promise | void => {\n return PlayerModule.unmute(this.nativeId);\n };\n\n /**\n * Sets the player's volume between 0 (silent) and 100 (max volume).\n *\n * @param volume - The volume level to set.\n */\n setVolume = (volume: number): Promise | void => {\n return PlayerModule.setVolume(this.nativeId, volume);\n };\n\n /**\n * @returns The player's current volume level.\n */\n getVolume = async (): Promise => {\n return (await PlayerModule.getVolume(this.nativeId)) ?? 0;\n };\n\n /**\n * @returns The current playback time in seconds.\n *\n * For VoD streams the returned time ranges between 0 and the duration of the asset.\n *\n * For live streams it can be specified if an absolute UNIX timestamp or a value\n * relative to the playback start should be returned.\n *\n * @param mode - The time mode to specify: an absolute UNIX timestamp ('absolute') or relative time ('relative').\n */\n getCurrentTime = async (\n mode: 'relative' | 'absolute' = 'absolute'\n ): Promise => {\n return (await PlayerModule.currentTime(this.nativeId, mode)) ?? 0;\n };\n\n /**\n * @returns The total duration in seconds of the current video or INFINITY if it’s a live stream.\n */\n getDuration = async (): Promise => {\n return (await PlayerModule.duration(this.nativeId)) ?? 0;\n };\n\n /**\n * @returns `true` if the player is muted.\n */\n isMuted = async (): Promise => {\n return (await PlayerModule.isMuted(this.nativeId)) ?? false;\n };\n\n /**\n * @returns `true` if the player is currently playing, i.e. has started and is not paused.\n */\n isPlaying = async (): Promise => {\n return (await PlayerModule.isPlaying(this.nativeId)) ?? false;\n };\n\n /**\n * @returns `true` if the player has started playback but it's currently paused.\n */\n isPaused = async (): Promise => {\n return (await PlayerModule.isPaused(this.nativeId)) ?? false;\n };\n\n /**\n * @returns `true` if the displayed video is a live stream.\n */\n isLive = async (): Promise => {\n return (await PlayerModule.isLive(this.nativeId)) ?? false;\n };\n\n /**\n * @remarks Only available for iOS devices.\n * @returns `true` when media is played externally using AirPlay.\n */\n isAirPlayActive = async (): Promise => {\n if (Platform.OS === 'android') {\n console.warn(\n `[Player ${this.nativeId}] Method isAirPlayActive is not available for Android. Only iOS devices.`\n );\n return false;\n }\n return (await PlayerModule.isAirPlayActive(this.nativeId)) ?? false;\n };\n\n /**\n * @remarks Only available for iOS devices.\n * @returns `true` when AirPlay is available.\n */\n isAirPlayAvailable = async (): Promise => {\n if (Platform.OS === 'android') {\n console.warn(\n `[Player ${this.nativeId}] Method isAirPlayAvailable is not available for Android. Only iOS devices.`\n );\n return false;\n }\n return (await PlayerModule.isAirPlayAvailable(this.nativeId)) ?? false;\n };\n\n /**\n * @returns The currently selected audio track or `null`.\n */\n getAudioTrack = async (): Promise => {\n return PlayerModule.getAudioTrack(this.nativeId);\n };\n\n /**\n * @returns An array containing {@link AudioTrack} objects for all available audio tracks.\n */\n getAvailableAudioTracks = async (): Promise => {\n return PlayerModule.getAvailableAudioTracks(this.nativeId);\n };\n\n /**\n * Sets the audio track to the ID specified by trackIdentifier. A list can be retrieved by calling getAvailableAudioTracks.\n *\n * @param trackIdentifier - The {@link AudioTrack.identifier} to be set.\n */\n setAudioTrack = async (trackIdentifier: string): Promise => {\n return PlayerModule.setAudioTrack(this.nativeId, trackIdentifier);\n };\n\n /**\n * @returns The currently selected {@link SubtitleTrack} or `null`.\n */\n getSubtitleTrack = async (): Promise => {\n return PlayerModule.getSubtitleTrack(this.nativeId);\n };\n\n /**\n * @returns An array containing SubtitleTrack objects for all available subtitle tracks.\n */\n getAvailableSubtitles = async (): Promise => {\n return PlayerModule.getAvailableSubtitles(this.nativeId);\n };\n\n /**\n * Sets the subtitle track to the ID specified by trackIdentifier. A list can be retrieved by calling getAvailableSubtitles.\n *\n * @param trackIdentifier - The {@link SubtitleTrack.identifier} to be set.\n */\n setSubtitleTrack = async (trackIdentifier?: string): Promise => {\n return PlayerModule.setSubtitleTrack(this.nativeId, trackIdentifier ?? '');\n };\n\n /**\n * Dynamically schedules the {@link AdItem} for playback.\n * Has no effect if there is no active playback session.\n *\n * @param adItem - Ad to be scheduled for playback.\n *\n * @remarks Platform: iOS, Android\n */\n scheduleAd = (adItem: AdItem): Promise | void => {\n return PlayerModule.scheduleAd(this.nativeId, adItem);\n };\n\n /**\n * Skips the current ad.\n * Has no effect if the current ad is not skippable or if no ad is being played back.\n *\n * @remarks Platform: iOS, Android\n */\n skipAd = (): Promise | void => {\n return PlayerModule.skipAd(this.nativeId);\n };\n\n /**\n * @returns `true` while an ad is being played back or when main content playback has been paused for ad playback.\n * @remarks Platform: iOS, Android\n */\n isAd = async (): Promise => {\n return (await PlayerModule.isAd(this.nativeId)) ?? false;\n };\n\n /**\n * The current time shift of the live stream in seconds. This value is always 0 if the active {@link Source} is not a\n * live stream or no sources are loaded.\n */\n getTimeShift = async (): Promise => {\n return (await PlayerModule.getTimeShift(this.nativeId)) ?? 0;\n };\n\n /**\n * The limit in seconds for time shifting. This value is either negative or 0 and it is always 0 if the active\n * {@link Source} is not a live stream or no sources are loaded.\n */\n getMaxTimeShift = async (): Promise => {\n return (await PlayerModule.getMaxTimeShift(this.nativeId)) ?? 0;\n };\n\n /**\n * Sets the upper bitrate boundary for video qualities. All qualities with a bitrate\n * that is higher than this threshold will not be eligible for automatic quality selection.\n *\n * Can be set to `null` for no limitation.\n */\n setMaxSelectableBitrate = (bitrate: number | null): Promise | void => {\n return PlayerModule.setMaxSelectableBitrate(this.nativeId, bitrate || -1);\n };\n\n /**\n * @returns a {@link Thumbnail} for the specified playback time for the currently active source if available.\n * Supported thumbnail formats are:\n * - `WebVtt` configured via {@link SourceConfig.thumbnailTrack}, on all supported platforms\n * - HLS `Image Media Playlist` in the multivariant playlist, Android-only\n * - DASH `Image Adaptation Set` as specified in DASH-IF IOP, Android-only\n * If a `WebVtt` thumbnail track is provided, any potential in-manifest thumbnails are ignored on Android.\n *\n * @param time - The time in seconds for which to retrieve the thumbnail.\n */\n getThumbnail = async (time: number): Promise => {\n return PlayerModule.getThumbnail(this.nativeId, time);\n };\n\n /**\n * Whether casting to a cast-compatible remote device is available. {@link CastAvailableEvent} signals when\n * casting becomes available.\n *\n * @remarks Platform: iOS, Android\n */\n isCastAvailable = async (): Promise => {\n return (await PlayerModule.isCastAvailable(this.nativeId)) ?? false;\n };\n\n /**\n * Whether video is currently being casted to a remote device and not played locally.\n *\n * @remarks Platform: iOS, Android\n */\n isCasting = async (): Promise => {\n return (await PlayerModule.isCasting(this.nativeId)) ?? false;\n };\n\n /**\n * Initiates casting the current video to a cast-compatible remote device. The user has to choose to which device it\n * should be sent.\n *\n * @remarks Platform: iOS, Android\n */\n castVideo = (): Promise | void => {\n return PlayerModule.castVideo(this.nativeId);\n };\n\n /**\n * Stops casting the current video. Has no effect if {@link Player.isCasting} is `false`.\n *\n * @remarks Platform: iOS, Android\n */\n castStop = (): Promise | void => {\n return PlayerModule.castStop(this.nativeId);\n };\n\n /**\n * Returns the currently selected video quality.\n * @returns The currently selected video quality.\n */\n getVideoQuality = async (): Promise => {\n return PlayerModule.getVideoQuality(this.nativeId);\n };\n\n /**\n * Returns an array containing all available video qualities the player can adapt between.\n * @returns An array containing all available video qualities the player can adapt between.\n */\n getAvailableVideoQualities = async (): Promise => {\n return PlayerModule.getAvailableVideoQualities(this.nativeId);\n };\n\n /**\n * Sets the video quality.\n * @remarks Platform: Android\n *\n * @param qualityId value obtained from {@link VideoQuality}'s `id` property, which can be obtained via `Player.getAvailableVideoQualities()` to select a specific quality. To use automatic quality selection, 'auto' can be passed here.\n */\n setVideoQuality = (qualityId: string): Promise | void => {\n if (Platform.OS !== 'android') {\n console.warn(\n `[Player ${this.nativeId}] Method setVideoQuality is not available for iOS and tvOS devices. Only Android devices.`\n );\n return Promise.resolve();\n }\n return PlayerModule.setVideoQuality(this.nativeId, qualityId);\n };\n\n /**\n * Sets the playback speed of the player. Fast forward, slow motion and reverse playback are supported.\n * @remarks\n * Platform: iOS, tvOS\n *\n * - Slow motion is indicated by values between `0` and `1`.\n * - Fast forward by values greater than `1`.\n * - Slow reverse is used by values between `0` and `-1`, and fast reverse is used by values less than `-1`. iOS and tvOS only.\n * - Negative values are ignored during Casting and on Android.\n * - During reverse playback the playback will continue until the beginning of the active source is\n * reached. When reaching the beginning of the source, playback will be paused and the playback\n * speed will be reset to its default value of `1`. No {@link PlaybackFinishedEvent} will be\n * emitted in this case.\n *\n * @param playbackSpeed - The playback speed to set.\n */\n setPlaybackSpeed = (playbackSpeed: number): Promise | void => {\n return PlayerModule.setPlaybackSpeed(this.nativeId, playbackSpeed);\n };\n\n /**\n * @see {@link setPlaybackSpeed} for details on which values playback speed can assume.\n * @returns The player's current playback speed.\n */\n getPlaybackSpeed = async (): Promise => {\n return (await PlayerModule.getPlaybackSpeed(this.nativeId)) ?? 0;\n };\n\n /**\n * Checks the possibility to play the media at specified playback speed.\n * @param playbackSpeed - The playback speed to check.\n * @returns `true` if it's possible to play the media at the specified playback speed, otherwise `false`. On Android it always returns `undefined`.\n * @remarks Platform: iOS, tvOS\n */\n canPlayAtPlaybackSpeed = async (\n playbackSpeed: number\n ): Promise => {\n if (Platform.OS === 'android') {\n console.warn(\n `[Player ${this.nativeId}] Method canPlayAtPlaybackSpeed is not available for Android. Only iOS and tvOS devices.`\n );\n return undefined;\n }\n return (\n (await PlayerModule.canPlayAtPlaybackSpeed(\n this.nativeId,\n playbackSpeed\n )) ?? false\n );\n };\n\n private maybeInitDecoderConfig = () => {\n if (this.config?.playbackConfig?.decoderConfig == null) {\n return;\n }\n if (Platform.OS === 'ios') {\n return;\n }\n\n this.decoderConfig = new DecoderConfigBridge(\n this.config.playbackConfig.decoderConfig\n );\n this.decoderConfig.initialize();\n };\n}\n"]} \ No newline at end of file diff --git a/build/playerConfig.d.ts b/build/playerConfig.d.ts new file mode 100644 index 00000000..31592b34 --- /dev/null +++ b/build/playerConfig.d.ts @@ -0,0 +1,83 @@ +import { AdvertisingConfig } from './advertising'; +import { AnalyticsConfig } from './analytics'; +import { StyleConfig } from './styleConfig'; +import { TweaksConfig } from './tweaksConfig'; +import { AdaptationConfig } from './adaptationConfig'; +import { RemoteControlConfig } from './remoteControlConfig'; +import { BufferConfig } from './bufferConfig'; +import { NativeInstanceConfig } from './nativeInstance'; +import { PlaybackConfig } from './playbackConfig'; +import { LiveConfig } from './liveConfig'; +import { NetworkConfig } from './network/networkConfig'; +import { MediaControlConfig } from './mediaControlConfig'; +/** + * Object used to configure a new `Player` instance. + */ +export interface PlayerConfig extends NativeInstanceConfig { + /** + * Bitmovin license key that can be found in the Bitmovin portal. + * If a license key is set here, it will be used instead of the license key found in the `Info.plist` and `AndroidManifest.xml`. + * @example + * Configuring the player license key from source code: + * ``` + * const player = new Player({ + * licenseKey: '\', + * }); + * ``` + * @example + * `licenseKey` can be safely omitted from source code if it has + * been configured in Info.plist/AndroidManifest.xml. + * ``` + * const player = new Player(); // omit `licenseKey` + * player.play(); // call methods and properties... + * ``` + */ + licenseKey?: string; + /** + * Configures playback behaviour. A default {@link PlaybackConfig} is set initially. + */ + playbackConfig?: PlaybackConfig; + /** + * Configures the visual presentation and behaviour of the player UI. A default {@link StyleConfig} is set initially. + */ + styleConfig?: StyleConfig; + /** + * Configures advertising functionality. A default {@link AdvertisingConfig} is set initially. + */ + advertisingConfig?: AdvertisingConfig; + /** + * Configures experimental features. A default {@link TweaksConfig} is set initially. + */ + tweaksConfig?: TweaksConfig; + /** + * Configures analytics functionality. + */ + analyticsConfig?: AnalyticsConfig; + /** + * Configures adaptation logic. + */ + adaptationConfig?: AdaptationConfig; + /** + * Configures remote playback functionality. + */ + remoteControlConfig?: RemoteControlConfig; + /** + * Configures buffer settings. A default {@link BufferConfig} is set initially. + */ + bufferConfig?: BufferConfig; + /** + * Configures behaviour when playing live content. A default {@link LiveConfig} is set initially. + */ + liveConfig?: LiveConfig; + /** + * Configures network request manipulation functionality. A default {@link NetworkConfig} is set initially. + */ + networkConfig?: NetworkConfig; + /** + * Configures the media control information for the application. This information will be displayed + * wherever current media information typically appears, such as the lock screen, in notifications, + * and inside the control center. + */ + mediaControlConfig?: MediaControlConfig; +} +//# sourceMappingURL=playerConfig.d.ts.map \ No newline at end of file diff --git a/build/playerConfig.d.ts.map b/build/playerConfig.d.ts.map new file mode 100644 index 00000000..9bd08fad --- /dev/null +++ b/build/playerConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"playerConfig.d.ts","sourceRoot":"","sources":["../src/playerConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,YAAa,SAAQ,oBAAoB;IACxD;;;;;;;;;;;;;;;;;OAiBG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC;;OAEG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;OAEG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC;;OAEG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;OAEG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC;;OAEG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC;;OAEG;IACH,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C;;OAEG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;OAEG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;;OAEG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC"} \ No newline at end of file diff --git a/build/playerConfig.js b/build/playerConfig.js new file mode 100644 index 00000000..a384f599 --- /dev/null +++ b/build/playerConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=playerConfig.js.map \ No newline at end of file diff --git a/build/playerConfig.js.map b/build/playerConfig.js.map new file mode 100644 index 00000000..085a8e62 --- /dev/null +++ b/build/playerConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"playerConfig.js","sourceRoot":"","sources":["../src/playerConfig.ts"],"names":[],"mappings":"","sourcesContent":["import { AdvertisingConfig } from './advertising';\nimport { AnalyticsConfig } from './analytics';\nimport { StyleConfig } from './styleConfig';\nimport { TweaksConfig } from './tweaksConfig';\nimport { AdaptationConfig } from './adaptationConfig';\nimport { RemoteControlConfig } from './remoteControlConfig';\nimport { BufferConfig } from './bufferConfig';\nimport { NativeInstanceConfig } from './nativeInstance';\nimport { PlaybackConfig } from './playbackConfig';\nimport { LiveConfig } from './liveConfig';\nimport { NetworkConfig } from './network/networkConfig';\nimport { MediaControlConfig } from './mediaControlConfig';\n\n/**\n * Object used to configure a new `Player` instance.\n */\nexport interface PlayerConfig extends NativeInstanceConfig {\n /**\n * Bitmovin license key that can be found in the Bitmovin portal.\n * If a license key is set here, it will be used instead of the license key found in the `Info.plist` and `AndroidManifest.xml`.\n * @example\n * Configuring the player license key from source code:\n * ```\n * const player = new Player({\n * licenseKey: '\\',\n * });\n * ```\n * @example\n * `licenseKey` can be safely omitted from source code if it has\n * been configured in Info.plist/AndroidManifest.xml.\n * ```\n * const player = new Player(); // omit `licenseKey`\n * player.play(); // call methods and properties...\n * ```\n */\n licenseKey?: string;\n /**\n * Configures playback behaviour. A default {@link PlaybackConfig} is set initially.\n */\n playbackConfig?: PlaybackConfig;\n /**\n * Configures the visual presentation and behaviour of the player UI. A default {@link StyleConfig} is set initially.\n */\n styleConfig?: StyleConfig;\n /**\n * Configures advertising functionality. A default {@link AdvertisingConfig} is set initially.\n */\n advertisingConfig?: AdvertisingConfig;\n /**\n * Configures experimental features. A default {@link TweaksConfig} is set initially.\n */\n tweaksConfig?: TweaksConfig;\n /**\n * Configures analytics functionality.\n */\n analyticsConfig?: AnalyticsConfig;\n /**\n * Configures adaptation logic.\n */\n adaptationConfig?: AdaptationConfig;\n /**\n * Configures remote playback functionality.\n */\n remoteControlConfig?: RemoteControlConfig;\n /**\n * Configures buffer settings. A default {@link BufferConfig} is set initially.\n */\n bufferConfig?: BufferConfig;\n /**\n * Configures behaviour when playing live content. A default {@link LiveConfig} is set initially.\n */\n liveConfig?: LiveConfig;\n /**\n * Configures network request manipulation functionality. A default {@link NetworkConfig} is set initially.\n */\n networkConfig?: NetworkConfig;\n /**\n * Configures the media control information for the application. This information will be displayed\n * wherever current media information typically appears, such as the lock screen, in notifications,\n * and inside the control center.\n */\n mediaControlConfig?: MediaControlConfig;\n}\n"]} \ No newline at end of file diff --git a/build/remoteControlConfig.d.ts b/build/remoteControlConfig.d.ts new file mode 100644 index 00000000..01206c07 --- /dev/null +++ b/build/remoteControlConfig.d.ts @@ -0,0 +1,38 @@ +/** + * Configures remote playback behavior. + */ +export interface RemoteControlConfig { + /** + * A URL to a CSS file the receiver app loads to style the receiver app. + * Default value is `null`, indicating that the default CSS of the receiver app will be used. + */ + receiverStylesheetUrl?: string | null; + /** + * A Map containing custom configuration values that are sent to the remote control receiver. + * Default value is an empty map. + */ + customReceiverConfig?: Record; + /** + * Whether casting is enabled. + * Default value is `true`. + * + * Has no effect if the `BitmovinCastManager` is not initialized before the `Player` is created with this configuration. + */ + isCastEnabled?: boolean; + /** + * Indicates whether cookies and credentials will be sent along manifest requests on the cast receiver + * Default value is `false`. + */ + sendManifestRequestsWithCredentials?: boolean; + /** + * Indicates whether cookies and credentials will be sent along segment requests on the cast receiver + * Default value is `false`. + */ + sendSegmentRequestsWithCredentials?: boolean; + /** + * Indicates whether cookies and credentials will be sent along DRM licence requests on the cast receiver + * Default value is `false`. + */ + sendDrmLicenseRequestsWithCredentials?: boolean; +} +//# sourceMappingURL=remoteControlConfig.d.ts.map \ No newline at end of file diff --git a/build/remoteControlConfig.d.ts.map b/build/remoteControlConfig.d.ts.map new file mode 100644 index 00000000..03bf5e4e --- /dev/null +++ b/build/remoteControlConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"remoteControlConfig.d.ts","sourceRoot":"","sources":["../src/remoteControlConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,mCAAmC,CAAC,EAAE,OAAO,CAAC;IAC9C;;;OAGG;IACH,kCAAkC,CAAC,EAAE,OAAO,CAAC;IAC7C;;;OAGG;IACH,qCAAqC,CAAC,EAAE,OAAO,CAAC;CACjD"} \ No newline at end of file diff --git a/build/remoteControlConfig.js b/build/remoteControlConfig.js new file mode 100644 index 00000000..a38d2358 --- /dev/null +++ b/build/remoteControlConfig.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=remoteControlConfig.js.map \ No newline at end of file diff --git a/build/remoteControlConfig.js.map b/build/remoteControlConfig.js.map new file mode 100644 index 00000000..31b5f86f --- /dev/null +++ b/build/remoteControlConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"remoteControlConfig.js","sourceRoot":"","sources":["../src/remoteControlConfig.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Configures remote playback behavior.\n */\nexport interface RemoteControlConfig {\n /**\n * A URL to a CSS file the receiver app loads to style the receiver app.\n * Default value is `null`, indicating that the default CSS of the receiver app will be used.\n */\n receiverStylesheetUrl?: string | null;\n /**\n * A Map containing custom configuration values that are sent to the remote control receiver.\n * Default value is an empty map.\n */\n customReceiverConfig?: Record;\n /**\n * Whether casting is enabled.\n * Default value is `true`.\n *\n * Has no effect if the `BitmovinCastManager` is not initialized before the `Player` is created with this configuration.\n */\n isCastEnabled?: boolean;\n /**\n * Indicates whether cookies and credentials will be sent along manifest requests on the cast receiver\n * Default value is `false`.\n */\n sendManifestRequestsWithCredentials?: boolean;\n /**\n * Indicates whether cookies and credentials will be sent along segment requests on the cast receiver\n * Default value is `false`.\n */\n sendSegmentRequestsWithCredentials?: boolean;\n /**\n * Indicates whether cookies and credentials will be sent along DRM licence requests on the cast receiver\n * Default value is `false`.\n */\n sendDrmLicenseRequestsWithCredentials?: boolean;\n}\n"]} \ No newline at end of file diff --git a/build/source.d.ts b/build/source.d.ts new file mode 100644 index 00000000..3350083d --- /dev/null +++ b/build/source.d.ts @@ -0,0 +1,216 @@ +import { DrmConfig } from './drm'; +import NativeInstance, { NativeInstanceConfig } from './nativeInstance'; +import { SideLoadedSubtitleTrack } from './subtitleTrack'; +import { Thumbnail } from './thumbnail'; +import { SourceMetadata } from './analytics'; +/** + * Types of media that can be handled by the player. + */ +export declare enum SourceType { + /** + * Indicates a missing source type. + */ + NONE = "none", + /** + * Indicates media type HLS. + */ + HLS = "hls", + /** + * Indicates media type DASH. + */ + DASH = "dash", + /** + * Indicates media type Progressive MP4. + */ + PROGRESSIVE = "progressive" +} +/** + * The different loading states a {@link Source} instance can be in. + */ +export declare enum LoadingState { + /** + * The source is unloaded. + */ + UNLOADED = 0, + /** + * The source is currently loading. + */ + LOADING = 1, + /** + * The source is loaded. + */ + LOADED = 2 +} +/** + * Types of SourceOptions. + */ +export interface SourceOptions { + /** + * The position where the stream should be started. + * Number can be positive or negative depending on the used `TimelineReferencePoint`. + * Invalid numbers will be corrected according to the stream boundaries. + * For VOD this is applied at the time the stream is loaded, for LIVE when playback starts. + */ + startOffset?: number; + /** + * Sets the Timeline reference point to calculate the startOffset from. + * Default for live: `TimelineReferencePoint.END`. + * Default for VOD: `TimelineReferencePoint.START`. + */ + startOffsetTimelineReference?: TimelineReferencePoint; +} +/** + Timeline reference point to calculate SourceOptions.startOffset from. + Default for live: TimelineReferencePoint.EBD Default for VOD: TimelineReferencePoint.START. + */ +export declare enum TimelineReferencePoint { + /** + * Relative offset will be calculated from the beginning of the stream or DVR window. + */ + START = "start", + /** + * Relative offset will be calculated from the end of the stream or the live edge in case of a live stream with DVR window. + */ + END = "end" +} +/** + * Represents a source configuration that be loaded into a player instance. + */ +export interface SourceConfig extends NativeInstanceConfig { + /** + * The url for this source configuration. + */ + url: string; + /** + * The `SourceType` for this configuration. + */ + type?: SourceType; + /** + * The title of the video source. + */ + title?: string; + /** + * The description of the video source. + */ + description?: string; + /** + * The URL to a preview image displayed until the video starts. + */ + poster?: string; + /** + * Indicates whether to show the poster image during playback. + * Useful, for example, for audio-only streams. + */ + isPosterPersistent?: boolean; + /** + * The DRM config for the source. + */ + drmConfig?: DrmConfig; + /** + * External subtitle tracks to be added into the player. + */ + subtitleTracks?: SideLoadedSubtitleTrack[]; + /** + * External thumbnails to be added into the player. + */ + thumbnailTrack?: string; + /** + * The optional custom metadata. Also sent to the cast receiver when loading the Source. + */ + metadata?: Record; + /** + * The `SourceOptions` for this configuration. + */ + options?: SourceOptions; + /** + * The `SourceMetadata` for the {@link Source} to setup custom analytics tracking + */ + analyticsSourceMetadata?: SourceMetadata; +} +/** + * The remote control config for a source. + * @remarks Platform: iOS + */ +export interface SourceRemoteControlConfig { + /** + * The `SourceConfig` for casting. + * Enables to play different content when casting. + * This can be useful when the remote playback device supports different streaming formats, + * DRM systems, etc. than the local device. + * If not set, the local source config will be used for casting. + */ + castSourceConfig?: SourceConfig | null; +} +/** + * Represents audio and video content that can be loaded into a player. + */ +export declare class Source extends NativeInstance { + /** + * The native DRM config reference of this source. + */ + private drm?; + /** + * The remote control config for this source. + * This is only supported on iOS. + * + * @remarks Platform: iOS + */ + remoteControl: SourceRemoteControlConfig | null; + /** + * Whether the native {@link Source} object has been created. + */ + isInitialized: boolean; + /** + * Whether the native {@link Source} object has been disposed. + */ + isDestroyed: boolean; + /** + * Allocates the native {@link Source} instance and its resources natively. + */ + initialize: () => Promise; + /** + * Destroys the native {@link Source} and releases all of its allocated resources. + */ + destroy: () => void; + /** + * The duration of the source in seconds if it’s a VoD or `INFINITY` if it’s a live stream. + * Default value is `0` if the duration is not available or not known. + */ + duration: () => Promise; + /** + * Whether the source is currently active in a player (i.e. playing back or paused). + * Only one source can be active in the same player instance at any time. + */ + isActive: () => Promise; + /** + * Whether the source is currently attached to a player instance. + */ + isAttachedToPlayer: () => Promise; + /** + * Metadata for the currently loaded source. + */ + metadata: () => Promise | null>; + /** + * Set metadata for the currently loaded source. + * Setting the metadata to `null` clears the metadata object in native source. + * + * @param metadata metadata to be set. + */ + setMetadata: (metadata: Record | null) => void; + /** + * The current `LoadingState` of the source. + */ + loadingState: () => Promise; + /** + * @returns a `Thumbnail` for the specified playback time if available. + * Supported thumbnail formats are: + * - `WebVtt` configured via {@link SourceConfig.thumbnailTrack}, on all supported platforms + * - HLS `Image Media Playlist` in the multivariant playlist, Android-only + * - DASH `Image Adaptation Set` as specified in DASH-IF IOP, Android-only + * If a `WebVtt` thumbnail track is provided, any potential in-manifest thumbnails are ignored on Android. + * + * @param time - The time in seconds for which to retrieve the thumbnail. + */ + getThumbnail: (time: number) => Promise; +} +//# sourceMappingURL=source.d.ts.map \ No newline at end of file diff --git a/build/source.d.ts.map b/build/source.d.ts.map new file mode 100644 index 00000000..e1f89d75 --- /dev/null +++ b/build/source.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/build/source.js b/build/source.js new file mode 100644 index 00000000..c6cf1e2b --- /dev/null +++ b/build/source.js @@ -0,0 +1,167 @@ +import { Drm } from './drm'; +import NativeInstance from './nativeInstance'; +import SourceModule from './modules/SourceModule'; +/** + * Types of media that can be handled by the player. + */ +export var SourceType; +(function (SourceType) { + /** + * Indicates a missing source type. + */ + SourceType["NONE"] = "none"; + /** + * Indicates media type HLS. + */ + SourceType["HLS"] = "hls"; + /** + * Indicates media type DASH. + */ + SourceType["DASH"] = "dash"; + /** + * Indicates media type Progressive MP4. + */ + SourceType["PROGRESSIVE"] = "progressive"; +})(SourceType || (SourceType = {})); +/** + * The different loading states a {@link Source} instance can be in. + */ +export var LoadingState; +(function (LoadingState) { + /** + * The source is unloaded. + */ + LoadingState[LoadingState["UNLOADED"] = 0] = "UNLOADED"; + /** + * The source is currently loading. + */ + LoadingState[LoadingState["LOADING"] = 1] = "LOADING"; + /** + * The source is loaded. + */ + LoadingState[LoadingState["LOADED"] = 2] = "LOADED"; +})(LoadingState || (LoadingState = {})); +/** + Timeline reference point to calculate SourceOptions.startOffset from. + Default for live: TimelineReferencePoint.EBD Default for VOD: TimelineReferencePoint.START. + */ +export var TimelineReferencePoint; +(function (TimelineReferencePoint) { + /** + * Relative offset will be calculated from the beginning of the stream or DVR window. + */ + TimelineReferencePoint["START"] = "start"; + /** + * Relative offset will be calculated from the end of the stream or the live edge in case of a live stream with DVR window. + */ + TimelineReferencePoint["END"] = "end"; +})(TimelineReferencePoint || (TimelineReferencePoint = {})); +/** + * Represents audio and video content that can be loaded into a player. + */ +export class Source extends NativeInstance { + /** + * The native DRM config reference of this source. + */ + drm; + /** + * The remote control config for this source. + * This is only supported on iOS. + * + * @remarks Platform: iOS + */ + remoteControl = null; + /** + * Whether the native {@link Source} object has been created. + */ + isInitialized = false; + /** + * Whether the native {@link Source} object has been disposed. + */ + isDestroyed = false; + /** + * Allocates the native {@link Source} instance and its resources natively. + */ + initialize = async () => { + if (!this.isInitialized) { + const sourceMetadata = this.config?.analyticsSourceMetadata; + if (this.config?.drmConfig) { + this.drm = new Drm(this.config.drmConfig); + this.drm.initialize(); + } + if (sourceMetadata) { + await SourceModule.initializeWithAnalyticsConfig(this.nativeId, this.drm?.nativeId, this.config, this.remoteControl || undefined, sourceMetadata); + } + else { + await SourceModule.initializeWithConfig(this.nativeId, this.drm?.nativeId, this.config, this.remoteControl || undefined); + } + this.isInitialized = true; + } + return Promise.resolve(); + }; + /** + * Destroys the native {@link Source} and releases all of its allocated resources. + */ + destroy = () => { + if (!this.isDestroyed) { + SourceModule.destroy(this.nativeId); + this.drm?.destroy(); + this.isDestroyed = true; + } + }; + /** + * The duration of the source in seconds if it’s a VoD or `INFINITY` if it’s a live stream. + * Default value is `0` if the duration is not available or not known. + */ + duration = async () => { + return (await SourceModule.duration(this.nativeId)) || 0; + }; + /** + * Whether the source is currently active in a player (i.e. playing back or paused). + * Only one source can be active in the same player instance at any time. + */ + isActive = async () => { + return (await SourceModule.isActive(this.nativeId)) ?? false; + }; + /** + * Whether the source is currently attached to a player instance. + */ + isAttachedToPlayer = async () => { + return (await SourceModule.isAttachedToPlayer(this.nativeId)) ?? false; + }; + /** + * Metadata for the currently loaded source. + */ + metadata = async () => { + return SourceModule.getMetadata(this.nativeId); + }; + /** + * Set metadata for the currently loaded source. + * Setting the metadata to `null` clears the metadata object in native source. + * + * @param metadata metadata to be set. + */ + setMetadata = (metadata) => { + SourceModule.setMetadata(this.nativeId, metadata); + }; + /** + * The current `LoadingState` of the source. + */ + loadingState = async () => { + return ((await SourceModule.loadingState(this.nativeId)) || LoadingState.UNLOADED); + }; + /** + * @returns a `Thumbnail` for the specified playback time if available. + * Supported thumbnail formats are: + * - `WebVtt` configured via {@link SourceConfig.thumbnailTrack}, on all supported platforms + * - HLS `Image Media Playlist` in the multivariant playlist, Android-only + * - DASH `Image Adaptation Set` as specified in DASH-IF IOP, Android-only + * If a `WebVtt` thumbnail track is provided, any potential in-manifest thumbnails are ignored on Android. + * + * @param time - The time in seconds for which to retrieve the thumbnail. + */ + getThumbnail = async (time) => { + return SourceModule.getThumbnail(this.nativeId, time); + }; +} +//# sourceMappingURL=source.js.map \ No newline at end of file diff --git a/build/source.js.map b/build/source.js.map new file mode 100644 index 00000000..adcc71ae --- /dev/null +++ b/build/source.js.map @@ -0,0 +1 @@ +{"version":3,"file":"source.js","sourceRoot":"","sources":["../src/source.ts"],"names":[],"mappings":"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","sourcesContent":["import { Drm, DrmConfig } from './drm';\nimport NativeInstance, { NativeInstanceConfig } from './nativeInstance';\nimport { SideLoadedSubtitleTrack } from './subtitleTrack';\nimport { Thumbnail } from './thumbnail';\nimport { SourceMetadata } from './analytics';\nimport SourceModule from './modules/SourceModule';\n\n/**\n * Types of media that can be handled by the player.\n */\nexport enum SourceType {\n /**\n * Indicates a missing source type.\n */\n NONE = 'none',\n /**\n * Indicates media type HLS.\n */\n HLS = 'hls',\n /**\n * Indicates media type DASH.\n */\n DASH = 'dash',\n /**\n * Indicates media type Progressive MP4.\n */\n PROGRESSIVE = 'progressive',\n}\n\n/**\n * The different loading states a {@link Source} instance can be in.\n */\nexport enum LoadingState {\n /**\n * The source is unloaded.\n */\n UNLOADED = 0,\n /**\n * The source is currently loading.\n */\n LOADING = 1,\n /**\n * The source is loaded.\n */\n LOADED = 2,\n}\n\n/**\n * Types of SourceOptions.\n */\nexport interface SourceOptions {\n /**\n * The position where the stream should be started.\n * Number can be positive or negative depending on the used `TimelineReferencePoint`.\n * Invalid numbers will be corrected according to the stream boundaries.\n * For VOD this is applied at the time the stream is loaded, for LIVE when playback starts.\n */\n startOffset?: number;\n /**\n * Sets the Timeline reference point to calculate the startOffset from.\n * Default for live: `TimelineReferencePoint.END`.\n * Default for VOD: `TimelineReferencePoint.START`.\n */\n startOffsetTimelineReference?: TimelineReferencePoint;\n}\n\n/**\n Timeline reference point to calculate SourceOptions.startOffset from.\n Default for live: TimelineReferencePoint.EBD Default for VOD: TimelineReferencePoint.START.\n */\nexport enum TimelineReferencePoint {\n /**\n * Relative offset will be calculated from the beginning of the stream or DVR window.\n */\n START = 'start',\n /**\n * Relative offset will be calculated from the end of the stream or the live edge in case of a live stream with DVR window.\n */\n END = 'end',\n}\n\n/**\n * Represents a source configuration that be loaded into a player instance.\n */\nexport interface SourceConfig extends NativeInstanceConfig {\n /**\n * The url for this source configuration.\n */\n url: string;\n /**\n * The `SourceType` for this configuration.\n */\n type?: SourceType;\n /**\n * The title of the video source.\n */\n title?: string;\n /**\n * The description of the video source.\n */\n description?: string;\n /**\n * The URL to a preview image displayed until the video starts.\n */\n poster?: string;\n /**\n * Indicates whether to show the poster image during playback.\n * Useful, for example, for audio-only streams.\n */\n isPosterPersistent?: boolean;\n /**\n * The DRM config for the source.\n */\n drmConfig?: DrmConfig;\n /**\n * External subtitle tracks to be added into the player.\n */\n subtitleTracks?: SideLoadedSubtitleTrack[];\n /**\n * External thumbnails to be added into the player.\n */\n thumbnailTrack?: string;\n /**\n * The optional custom metadata. Also sent to the cast receiver when loading the Source.\n */\n metadata?: Record;\n /**\n * The `SourceOptions` for this configuration.\n */\n options?: SourceOptions;\n /**\n * The `SourceMetadata` for the {@link Source} to setup custom analytics tracking\n */\n analyticsSourceMetadata?: SourceMetadata;\n}\n\n/**\n * The remote control config for a source.\n * @remarks Platform: iOS\n */\nexport interface SourceRemoteControlConfig {\n /**\n * The `SourceConfig` for casting.\n * Enables to play different content when casting.\n * This can be useful when the remote playback device supports different streaming formats,\n * DRM systems, etc. than the local device.\n * If not set, the local source config will be used for casting.\n */\n castSourceConfig?: SourceConfig | null;\n}\n\n/**\n * Represents audio and video content that can be loaded into a player.\n */\nexport class Source extends NativeInstance {\n /**\n * The native DRM config reference of this source.\n */\n private drm?: Drm;\n /**\n * The remote control config for this source.\n * This is only supported on iOS.\n *\n * @remarks Platform: iOS\n */\n remoteControl: SourceRemoteControlConfig | null = null;\n /**\n * Whether the native {@link Source} object has been created.\n */\n isInitialized = false;\n /**\n * Whether the native {@link Source} object has been disposed.\n */\n isDestroyed = false;\n\n /**\n * Allocates the native {@link Source} instance and its resources natively.\n */\n initialize = async (): Promise => {\n if (!this.isInitialized) {\n const sourceMetadata = this.config?.analyticsSourceMetadata;\n if (this.config?.drmConfig) {\n this.drm = new Drm(this.config.drmConfig);\n this.drm.initialize();\n }\n if (sourceMetadata) {\n await SourceModule.initializeWithAnalyticsConfig(\n this.nativeId,\n this.drm?.nativeId,\n this.config,\n this.remoteControl || undefined,\n sourceMetadata\n );\n } else {\n await SourceModule.initializeWithConfig(\n this.nativeId,\n this.drm?.nativeId,\n this.config,\n this.remoteControl || undefined\n );\n }\n this.isInitialized = true;\n }\n return Promise.resolve();\n };\n\n /**\n * Destroys the native {@link Source} and releases all of its allocated resources.\n */\n destroy = () => {\n if (!this.isDestroyed) {\n SourceModule.destroy(this.nativeId);\n this.drm?.destroy();\n this.isDestroyed = true;\n }\n };\n\n /**\n * The duration of the source in seconds if it’s a VoD or `INFINITY` if it’s a live stream.\n * Default value is `0` if the duration is not available or not known.\n */\n duration = async (): Promise => {\n return (await SourceModule.duration(this.nativeId)) || 0;\n };\n\n /**\n * Whether the source is currently active in a player (i.e. playing back or paused).\n * Only one source can be active in the same player instance at any time.\n */\n isActive = async (): Promise => {\n return (await SourceModule.isActive(this.nativeId)) ?? false;\n };\n\n /**\n * Whether the source is currently attached to a player instance.\n */\n isAttachedToPlayer = async (): Promise => {\n return (await SourceModule.isAttachedToPlayer(this.nativeId)) ?? false;\n };\n\n /**\n * Metadata for the currently loaded source.\n */\n metadata = async (): Promise | null> => {\n return SourceModule.getMetadata(this.nativeId);\n };\n\n /**\n * Set metadata for the currently loaded source.\n * Setting the metadata to `null` clears the metadata object in native source.\n *\n * @param metadata metadata to be set.\n */\n setMetadata = (metadata: Record | null): void => {\n SourceModule.setMetadata(this.nativeId, metadata);\n };\n\n /**\n * The current `LoadingState` of the source.\n */\n loadingState = async (): Promise => {\n return (\n (await SourceModule.loadingState(this.nativeId)) || LoadingState.UNLOADED\n );\n };\n\n /**\n * @returns a `Thumbnail` for the specified playback time if available.\n * Supported thumbnail formats are:\n * - `WebVtt` configured via {@link SourceConfig.thumbnailTrack}, on all supported platforms\n * - HLS `Image Media Playlist` in the multivariant playlist, Android-only\n * - DASH `Image Adaptation Set` as specified in DASH-IF IOP, Android-only\n * If a `WebVtt` thumbnail track is provided, any potential in-manifest thumbnails are ignored on Android.\n *\n * @param time - The time in seconds for which to retrieve the thumbnail.\n */\n getThumbnail = async (time: number): Promise => {\n return SourceModule.getThumbnail(this.nativeId, time);\n };\n}\n"]} \ No newline at end of file diff --git a/build/styleConfig.d.ts b/build/styleConfig.d.ts new file mode 100644 index 00000000..9c7263b0 --- /dev/null +++ b/build/styleConfig.d.ts @@ -0,0 +1,120 @@ +/** + * Contains config values which can be used to alter the visual presentation and behaviour of the player UI. + */ +export interface StyleConfig { + /** + * Sets if the UI should be enabled or not. Default value is `true`. + * @example + * ``` + * const player = new Player({ + * styleConfig: { + * isUiEnabled: false, + * }, + * }); + * ``` + */ + isUiEnabled?: boolean; + /** + * Sets which user interface type should be used. + * Default value is `UserInterfaceType.bitmovin` on `iOS` and `UserInterfaceType.system` on `tvOS`. + * This setting only applies if `StyleConfig.isUiEnabled` is set to true. + * @example + * ``` + * const player = new Player({ + * styleConfig: { + * userInterfaceType: UserInterfaceType.System, + * }, + * }); + * ``` + * @remarks Platform: iOS, tvOS + */ + userInterfaceType?: UserInterfaceType; + /** + * Sets the CSS file that will be used for the UI. The default CSS file will be completely replaced by the CSS file set with this property. + * @example + * ``` + * const player = new Player({ + * styleConfig: { + * playerUiCss: 'https://domain.tld/path/to/bitmovinplayer-ui.css', + * }, + * }); + * ``` + * @remarks Platform: iOS, Android + */ + playerUiCss?: string; + /** + * Sets a CSS file which contains supplemental styles for the player UI. These styles will be added to the default CSS file or the CSS file set with `StyleConfig.playerUiCss`. + * @example + * ``` + * const player = new Player({ + * styleConfig: { + * supplementalPlayerUiCss: 'https://domain.tld/path/to/bitmovinplayer-supplemental-ui.css', + * }, + * }); + * ``` + * @remarks Platform: iOS, Android + */ + supplementalPlayerUiCss?: string; + /** + * Sets the JS file that will be used for the UI. The default JS file will be completely replaced by the JS file set with this property. + * @example + * ``` + * const player = new Player({ + * styleConfig: { + * playerUiJs: 'https://domain.tld/path/to/bitmovinplayer-ui.js', + * }, + * }); + * ``` + * @remarks Platform: iOS, Android + */ + playerUiJs?: string; + /** + * Determines how the video content is scaled or stretched within the parent container’s bounds. Possible values are defined in `ScalingMode`. + * Default value is `ScalingMode.fit`. + * @example + * ``` + * const player = new Player({ + * styleConfig: { + * scalingMode: ScalingMode.Zoom, + * }, + * }); + * ``` + */ + scalingMode?: ScalingMode; +} +/** + * Specifies how the video content is scaled or stretched. + */ +export declare enum ScalingMode { + /** + * Specifies that the player should preserve the video’s aspect ratio and fit the video within the container's bounds. + */ + Fit = "Fit", + /** + * Specifies that the video should be stretched to fill the container’s bounds. The aspect ratio may not be preserved. + */ + Stretch = "Stretch", + /** + * Specifies that the player should preserve the video’s aspect ratio and fill the container’s bounds. + */ + Zoom = "Zoom" +} +/** + * Indicates which type of UI should be used. + */ +export declare enum UserInterfaceType { + /** + * Indicates that Bitmovin's customizable UI should be used. + */ + Bitmovin = "Bitmovin", + /** + * Indicates that the system UI should be used. + * @remarks Platform: iOS, tvOS + */ + System = "System", + /** + * Indicates that only subtitles should be displayed along with the video content. + */ + Subtitle = "Subtitle" +} +//# sourceMappingURL=styleConfig.d.ts.map \ No newline at end of file diff --git a/build/styleConfig.d.ts.map b/build/styleConfig.d.ts.map new file mode 100644 index 00000000..e6f1ee38 --- /dev/null +++ b/build/styleConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"styleConfig.d.ts","sourceRoot":"","sources":["../src/styleConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;;;;;;;;OAaG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;;;;;OAWG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED;;GAEG;AACH,oBAAY,WAAW;IACrB;;OAEG;IACH,GAAG,QAAQ;IACX;;OAEG;IACH,OAAO,YAAY;IACnB;;OAEG;IACH,IAAI,SAAS;CACd;AAED;;GAEG;AACH,oBAAY,iBAAiB;IAC3B;;OAEG;IACH,QAAQ,aAAa;IACrB;;;OAGG;IACH,MAAM,WAAW;IACjB;;OAEG;IACH,QAAQ,aAAa;CACtB"} \ No newline at end of file diff --git a/build/styleConfig.js b/build/styleConfig.js new file mode 100644 index 00000000..58f430b7 --- /dev/null +++ b/build/styleConfig.js @@ -0,0 +1,38 @@ +/** + * Specifies how the video content is scaled or stretched. + */ +export var ScalingMode; +(function (ScalingMode) { + /** + * Specifies that the player should preserve the video’s aspect ratio and fit the video within the container's bounds. + */ + ScalingMode["Fit"] = "Fit"; + /** + * Specifies that the video should be stretched to fill the container’s bounds. The aspect ratio may not be preserved. + */ + ScalingMode["Stretch"] = "Stretch"; + /** + * Specifies that the player should preserve the video’s aspect ratio and fill the container’s bounds. + */ + ScalingMode["Zoom"] = "Zoom"; +})(ScalingMode || (ScalingMode = {})); +/** + * Indicates which type of UI should be used. + */ +export var UserInterfaceType; +(function (UserInterfaceType) { + /** + * Indicates that Bitmovin's customizable UI should be used. + */ + UserInterfaceType["Bitmovin"] = "Bitmovin"; + /** + * Indicates that the system UI should be used. + * @remarks Platform: iOS, tvOS + */ + UserInterfaceType["System"] = "System"; + /** + * Indicates that only subtitles should be displayed along with the video content. + */ + UserInterfaceType["Subtitle"] = "Subtitle"; +})(UserInterfaceType || (UserInterfaceType = {})); +//# sourceMappingURL=styleConfig.js.map \ No newline at end of file diff --git a/build/styleConfig.js.map b/build/styleConfig.js.map new file mode 100644 index 00000000..98be5283 --- /dev/null +++ b/build/styleConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"styleConfig.js","sourceRoot":"","sources":["../src/styleConfig.ts"],"names":[],"mappings":"AAqFA;;GAEG;AACH,MAAM,CAAN,IAAY,WAaX;AAbD,WAAY,WAAW;IACrB;;OAEG;IACH,0BAAW,CAAA;IACX;;OAEG;IACH,kCAAmB,CAAA;IACnB;;OAEG;IACH,4BAAa,CAAA;AACf,CAAC,EAbW,WAAW,KAAX,WAAW,QAatB;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,iBAcX;AAdD,WAAY,iBAAiB;IAC3B;;OAEG;IACH,0CAAqB,CAAA;IACrB;;;OAGG;IACH,sCAAiB,CAAA;IACjB;;OAEG;IACH,0CAAqB,CAAA;AACvB,CAAC,EAdW,iBAAiB,KAAjB,iBAAiB,QAc5B","sourcesContent":["/**\n * Contains config values which can be used to alter the visual presentation and behaviour of the player UI.\n */\nexport interface StyleConfig {\n /**\n * Sets if the UI should be enabled or not. Default value is `true`.\n * @example\n * ```\n * const player = new Player({\n * styleConfig: {\n * isUiEnabled: false,\n * },\n * });\n * ```\n */\n isUiEnabled?: boolean;\n /**\n * Sets which user interface type should be used.\n * Default value is `UserInterfaceType.bitmovin` on `iOS` and `UserInterfaceType.system` on `tvOS`.\n * This setting only applies if `StyleConfig.isUiEnabled` is set to true.\n * @example\n * ```\n * const player = new Player({\n * styleConfig: {\n * userInterfaceType: UserInterfaceType.System,\n * },\n * });\n * ```\n * @remarks Platform: iOS, tvOS\n */\n userInterfaceType?: UserInterfaceType;\n /**\n * Sets the CSS file that will be used for the UI. The default CSS file will be completely replaced by the CSS file set with this property.\n * @example\n * ```\n * const player = new Player({\n * styleConfig: {\n * playerUiCss: 'https://domain.tld/path/to/bitmovinplayer-ui.css',\n * },\n * });\n * ```\n * @remarks Platform: iOS, Android\n */\n playerUiCss?: string;\n /**\n * Sets a CSS file which contains supplemental styles for the player UI. These styles will be added to the default CSS file or the CSS file set with `StyleConfig.playerUiCss`.\n * @example\n * ```\n * const player = new Player({\n * styleConfig: {\n * supplementalPlayerUiCss: 'https://domain.tld/path/to/bitmovinplayer-supplemental-ui.css',\n * },\n * });\n * ```\n * @remarks Platform: iOS, Android\n */\n supplementalPlayerUiCss?: string;\n /**\n * Sets the JS file that will be used for the UI. The default JS file will be completely replaced by the JS file set with this property.\n * @example\n * ```\n * const player = new Player({\n * styleConfig: {\n * playerUiJs: 'https://domain.tld/path/to/bitmovinplayer-ui.js',\n * },\n * });\n * ```\n * @remarks Platform: iOS, Android\n */\n playerUiJs?: string;\n /**\n * Determines how the video content is scaled or stretched within the parent container’s bounds. Possible values are defined in `ScalingMode`.\n * Default value is `ScalingMode.fit`.\n * @example\n * ```\n * const player = new Player({\n * styleConfig: {\n * scalingMode: ScalingMode.Zoom,\n * },\n * });\n * ```\n */\n scalingMode?: ScalingMode;\n}\n\n/**\n * Specifies how the video content is scaled or stretched.\n */\nexport enum ScalingMode {\n /**\n * Specifies that the player should preserve the video’s aspect ratio and fit the video within the container's bounds.\n */\n Fit = 'Fit',\n /**\n * Specifies that the video should be stretched to fill the container’s bounds. The aspect ratio may not be preserved.\n */\n Stretch = 'Stretch',\n /**\n * Specifies that the player should preserve the video’s aspect ratio and fill the container’s bounds.\n */\n Zoom = 'Zoom',\n}\n\n/**\n * Indicates which type of UI should be used.\n */\nexport enum UserInterfaceType {\n /**\n * Indicates that Bitmovin's customizable UI should be used.\n */\n Bitmovin = 'Bitmovin',\n /**\n * Indicates that the system UI should be used.\n * @remarks Platform: iOS, tvOS\n */\n System = 'System',\n /**\n * Indicates that only subtitles should be displayed along with the video content.\n */\n Subtitle = 'Subtitle',\n}\n"]} \ No newline at end of file diff --git a/build/subtitleFormat.d.ts b/build/subtitleFormat.d.ts new file mode 100644 index 00000000..3a9ecc40 --- /dev/null +++ b/build/subtitleFormat.d.ts @@ -0,0 +1,27 @@ +/** + * Supported subtitle/caption file formats. + * @remarks Platform: Android, iOS, tvOS + */ +export declare enum SubtitleFormat { + /** + * Closed Captioning (CEA) subtitle format. + * @remarks Platform: Android, iOS, tvOS + */ + CEA = "cea", + /** + * Timed Text Markup Language (TTML) subtitle format. + * @remarks Platform: Android, iOS, tvOS + */ + TTML = "ttml", + /** + * Web Video Text Tracks Format (WebVTT) subtitle format. + * @remarks Platform: Android, iOS, tvOS + */ + VTT = "vtt", + /** + * SubRip (SRT) subtitle format. + * @remarks Platform: Android, iOS, tvOS + */ + SRT = "srt" +} +//# sourceMappingURL=subtitleFormat.d.ts.map \ No newline at end of file diff --git a/build/subtitleFormat.d.ts.map b/build/subtitleFormat.d.ts.map new file mode 100644 index 00000000..a5c2e381 --- /dev/null +++ b/build/subtitleFormat.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"subtitleFormat.d.ts","sourceRoot":"","sources":["../src/subtitleFormat.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,oBAAY,cAAc;IACxB;;;OAGG;IACH,GAAG,QAAQ;IACX;;;OAGG;IACH,IAAI,SAAS;IACb;;;OAGG;IACH,GAAG,QAAQ;IACX;;;OAGG;IACH,GAAG,QAAQ;CACZ"} \ No newline at end of file diff --git a/build/subtitleFormat.js b/build/subtitleFormat.js new file mode 100644 index 00000000..53526374 --- /dev/null +++ b/build/subtitleFormat.js @@ -0,0 +1,28 @@ +/** + * Supported subtitle/caption file formats. + * @remarks Platform: Android, iOS, tvOS + */ +export var SubtitleFormat; +(function (SubtitleFormat) { + /** + * Closed Captioning (CEA) subtitle format. + * @remarks Platform: Android, iOS, tvOS + */ + SubtitleFormat["CEA"] = "cea"; + /** + * Timed Text Markup Language (TTML) subtitle format. + * @remarks Platform: Android, iOS, tvOS + */ + SubtitleFormat["TTML"] = "ttml"; + /** + * Web Video Text Tracks Format (WebVTT) subtitle format. + * @remarks Platform: Android, iOS, tvOS + */ + SubtitleFormat["VTT"] = "vtt"; + /** + * SubRip (SRT) subtitle format. + * @remarks Platform: Android, iOS, tvOS + */ + SubtitleFormat["SRT"] = "srt"; +})(SubtitleFormat || (SubtitleFormat = {})); +//# sourceMappingURL=subtitleFormat.js.map \ No newline at end of file diff --git a/build/subtitleFormat.js.map b/build/subtitleFormat.js.map new file mode 100644 index 00000000..45e7ffa3 --- /dev/null +++ b/build/subtitleFormat.js.map @@ -0,0 +1 @@ +{"version":3,"file":"subtitleFormat.js","sourceRoot":"","sources":["../src/subtitleFormat.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAN,IAAY,cAqBX;AArBD,WAAY,cAAc;IACxB;;;OAGG;IACH,6BAAW,CAAA;IACX;;;OAGG;IACH,+BAAa,CAAA;IACb;;;OAGG;IACH,6BAAW,CAAA;IACX;;;OAGG;IACH,6BAAW,CAAA;AACb,CAAC,EArBW,cAAc,KAAd,cAAc,QAqBzB","sourcesContent":["/**\n * Supported subtitle/caption file formats.\n * @remarks Platform: Android, iOS, tvOS\n */\nexport enum SubtitleFormat {\n /**\n * Closed Captioning (CEA) subtitle format.\n * @remarks Platform: Android, iOS, tvOS\n */\n CEA = 'cea',\n /**\n * Timed Text Markup Language (TTML) subtitle format.\n * @remarks Platform: Android, iOS, tvOS\n */\n TTML = 'ttml',\n /**\n * Web Video Text Tracks Format (WebVTT) subtitle format.\n * @remarks Platform: Android, iOS, tvOS\n */\n VTT = 'vtt',\n /**\n * SubRip (SRT) subtitle format.\n * @remarks Platform: Android, iOS, tvOS\n */\n SRT = 'srt',\n}\n"]} \ No newline at end of file diff --git a/build/subtitleTrack.d.ts b/build/subtitleTrack.d.ts new file mode 100644 index 00000000..aff5cf14 --- /dev/null +++ b/build/subtitleTrack.d.ts @@ -0,0 +1,55 @@ +import { MediaTrackRole } from './mediaTrackRole'; +import { SubtitleFormat } from './subtitleFormat'; +/** + * Describes a subtitle track. + * @remarks Platform: Android, iOS, tvOS + */ +export interface SubtitleTrack { + /** + * The URL to the timed file, e.g. WebVTT file. + */ + url?: string; + /** + * The label for this track. + */ + label?: string; + /** + * The unique identifier for this track. If no value is provided, a random UUIDv4 will be generated for it. + */ + identifier?: string; + /** + * Specifies the file format to be used by this track. + */ + format?: SubtitleFormat; + /** + * If set to true, this track would be considered as default. Default is `false`. + */ + isDefault?: boolean; + /** + * Tells if a subtitle track is forced. If set to `true` it means that the player should automatically + * select and switch this subtitle according to the selected audio language. Forced subtitles do + * not appear in `Player.getAvailableSubtitles`. + * + * Default is `false`. + */ + isForced?: boolean; + /** + * The IETF BCP 47 language tag associated with this track, e.g. `pt`, `en`, `es` etc. + */ + language?: string; + /** + * An array of {@link MediaTrackRole} objects, each describing a specific role or characteristic of the subtitle track. + * This property provides a unified way to understand track purposes (e.g., for accessibility) across platforms. + */ + roles?: MediaTrackRole[]; +} +/** + * A subtitle track that can be added to `SourceConfig.subtitleTracks`. + */ +export interface SideLoadedSubtitleTrack extends SubtitleTrack { + url: string; + label: string; + language: string; + format: SubtitleFormat; +} +//# sourceMappingURL=subtitleTrack.d.ts.map \ No newline at end of file diff --git a/build/subtitleTrack.d.ts.map b/build/subtitleTrack.d.ts.map new file mode 100644 index 00000000..f3311e2f --- /dev/null +++ b/build/subtitleTrack.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"subtitleTrack.d.ts","sourceRoot":"","sources":["../src/subtitleTrack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,aAAa;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,cAAc,CAAC;CACxB"} \ No newline at end of file diff --git a/build/subtitleTrack.js b/build/subtitleTrack.js new file mode 100644 index 00000000..0c70bd1e --- /dev/null +++ b/build/subtitleTrack.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=subtitleTrack.js.map \ No newline at end of file diff --git a/build/subtitleTrack.js.map b/build/subtitleTrack.js.map new file mode 100644 index 00000000..54e8c985 --- /dev/null +++ b/build/subtitleTrack.js.map @@ -0,0 +1 @@ +{"version":3,"file":"subtitleTrack.js","sourceRoot":"","sources":["../src/subtitleTrack.ts"],"names":[],"mappings":"","sourcesContent":["import { MediaTrackRole } from './mediaTrackRole';\nimport { SubtitleFormat } from './subtitleFormat';\n\n/**\n * Describes a subtitle track.\n * @remarks Platform: Android, iOS, tvOS\n */\nexport interface SubtitleTrack {\n /**\n * The URL to the timed file, e.g. WebVTT file.\n */\n url?: string;\n /**\n * The label for this track.\n */\n label?: string;\n /**\n * The unique identifier for this track. If no value is provided, a random UUIDv4 will be generated for it.\n */\n identifier?: string;\n /**\n * Specifies the file format to be used by this track.\n */\n format?: SubtitleFormat;\n /**\n * If set to true, this track would be considered as default. Default is `false`.\n */\n isDefault?: boolean;\n /**\n * Tells if a subtitle track is forced. If set to `true` it means that the player should automatically\n * select and switch this subtitle according to the selected audio language. Forced subtitles do\n * not appear in `Player.getAvailableSubtitles`.\n *\n * Default is `false`.\n */\n isForced?: boolean;\n /**\n * The IETF BCP 47 language tag associated with this track, e.g. `pt`, `en`, `es` etc.\n */\n language?: string;\n /**\n * An array of {@link MediaTrackRole} objects, each describing a specific role or characteristic of the subtitle track.\n * This property provides a unified way to understand track purposes (e.g., for accessibility) across platforms.\n */\n roles?: MediaTrackRole[];\n}\n\n/**\n * A subtitle track that can be added to `SourceConfig.subtitleTracks`.\n */\nexport interface SideLoadedSubtitleTrack extends SubtitleTrack {\n url: string;\n label: string;\n language: string;\n format: SubtitleFormat;\n}\n"]} \ No newline at end of file diff --git a/build/thumbnail.d.ts b/build/thumbnail.d.ts new file mode 100644 index 00000000..5f4c77a1 --- /dev/null +++ b/build/thumbnail.d.ts @@ -0,0 +1,38 @@ +/** + * Represents a VTT thumbnail. + */ +export interface Thumbnail { + /** + * The start time of the thumbnail. + */ + start: number; + /** + * The end time of the thumbnail. + */ + end: number; + /** + * The raw cue data. + */ + text: string; + /** + * The URL of the spritesheet + */ + url: string; + /** + * The horizontal offset of the thumbnail in its spritesheet + */ + x: number; + /** + * The vertical offset of the thumbnail in its spritesheet + */ + y: number; + /** + * The width of the thumbnail + */ + width: number; + /** + * The height of the thumbnail + */ + height: number; +} +//# sourceMappingURL=thumbnail.d.ts.map \ No newline at end of file diff --git a/build/thumbnail.d.ts.map b/build/thumbnail.d.ts.map new file mode 100644 index 00000000..ade6665a --- /dev/null +++ b/build/thumbnail.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"thumbnail.d.ts","sourceRoot":"","sources":["../src/thumbnail.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;OAEG;IACH,CAAC,EAAE,MAAM,CAAC;IACV;;OAEG;IACH,CAAC,EAAE,MAAM,CAAC;IACV;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB"} \ No newline at end of file diff --git a/build/thumbnail.js b/build/thumbnail.js new file mode 100644 index 00000000..0ae6d61f --- /dev/null +++ b/build/thumbnail.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=thumbnail.js.map \ No newline at end of file diff --git a/build/thumbnail.js.map b/build/thumbnail.js.map new file mode 100644 index 00000000..5c299006 --- /dev/null +++ b/build/thumbnail.js.map @@ -0,0 +1 @@ +{"version":3,"file":"thumbnail.js","sourceRoot":"","sources":["../src/thumbnail.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Represents a VTT thumbnail.\n */\nexport interface Thumbnail {\n /**\n * The start time of the thumbnail.\n */\n start: number;\n /**\n * The end time of the thumbnail.\n */\n end: number;\n /**\n * The raw cue data.\n */\n text: string;\n /**\n * The URL of the spritesheet\n */\n url: string;\n /**\n * The horizontal offset of the thumbnail in its spritesheet\n */\n x: number;\n /**\n * The vertical offset of the thumbnail in its spritesheet\n */\n y: number;\n /**\n * The width of the thumbnail\n */\n width: number;\n /**\n * The height of the thumbnail\n */\n height: number;\n}\n"]} \ No newline at end of file diff --git a/build/tweaksConfig.d.ts b/build/tweaksConfig.d.ts new file mode 100644 index 00000000..2c8734ef --- /dev/null +++ b/build/tweaksConfig.d.ts @@ -0,0 +1,197 @@ +/** + * When switching the video quality, the video decoder's configuration might change + * as the player can't always know if the codec supports such configuration change, it destroys and recreates it. + * This behaviour can cause brief black screens when switching between video qualities as codec recreation can be slow. + * + * If a codec is know to support a given configuration change without issues, + * the configuration can be added to the `TweaksConfig.forceReuseVideoCodecReasons` + * to always reuse the video codec and avoid the black screen. + */ +export declare enum ForceReuseVideoCodecReason { + /** + * The new video quality color information is not compatible. + */ + ColorInfoMismatch = "ColorInfoMismatch", + /** + * The new video quality exceed the decoder's configured maximum sample size. + */ + MaxInputSizeExceeded = "MaxInputSizeExceeded", + /** + * The new video quality exceed the decoder's configured maximum resolution. + */ + MaxResolutionExceeded = "MaxResolutionExceeded" +} +/** + * This configuration is used as an incubator for experimental features. Tweaks are not officially + * supported and are not guaranteed to be stable, i.e. their naming, functionality and API can + * change at any time within the tweaks or when being promoted to an official feature and moved + * into its final configuration namespace. + */ +export interface TweaksConfig { + /** + * The frequency in seconds `onTimeChanged` is called with `TimeChangedEvent`s. + * + * Default value in iOS is `1.0`. + * Default value in Android is `0.2`. + * + * @remarks Platform: iOS, Android + */ + timeChangedInterval?: number; + /** + * If enabled, HLS playlists will be parsed and additional features and events are enabled. This includes: + * + * - MetadataEvents carrying segment-specific metadata for custom HLS tags, like `#EXT-X-SCTE35` + * - MetadataParsedEvents carrying segment-specific metadata for custom HLS tags, like `#EXT-X-SCTE35` + * - DrmDataParsedEvents when a `#EXT-X-KEY` is found + * - `Player.availableVideoQualities` includes additional information + * - Automatic retries when HLS playlist requests failed with non-2xx HTTP status code + * + * Default is false. + * + * @remarks Platform: iOS + */ + isNativeHlsParsingEnabled?: boolean; + /** + * If enabled, playlists will be downloaded by the Bitmovin Player SDK instead of AVFoundation. + * This enables additional features and events, like: + * + * - DownloadFinishedEvents for playlist downloads. + * - SourceWarningEvents when no `#EXT-X-PLAYLIST-TYPE` is found If set to false, enabling + * nativeHlsParsingEnabled won’t have any effect. + * + * Default is true. + * + * @remarks Platform: iOS + */ + isCustomHlsLoadingEnabled?: boolean; + /** + * The threshold which will be applied when seeking to the end in seconds. This value will be used + * to calculate the maximum seekable time when calling `player.seek(time:)` or `player.playlist.seek(source:time:)`, + * so the maximum value will be duration - seekToEndThreshold. + * + * This is useful if the duration of the segments does not match the duration specified in the + * manifest. In this case, if we try to seek to the end, AVPlayer could get stuck and might stall + * forever Therefore increasing this value could help. + * + * Default is 0.5. + * + * @remarks Platform: iOS + */ + seekToEndThreshold?: number; + /** + * Specifies the player behaviour when `Player.play` is called. Default is 'relaxed'. + * + * - 'relaxed': Starts playback when enough media data is buffered and continuous playback without stalling can be ensured. If insufficient media data is buffered for playback to start, the player will act as if the buffer became empty during playback. + * - 'aggressive': When the buffer is not empty, this setting will cause the player to start playback of available media immediately. If insufficient media data is buffered for playback to start, the player will act as if the buffer became empty during playback. + * + * @remarks Platform: iOS + */ + playbackStartBehaviour?: 'relaxed' | 'aggressive'; + /** + * Specifies the player behaviour when stalling should be exited. Default is 'relaxed'. + * + * - 'relaxed': The player will wait until the buffer is filled that it can, most likely, ensure continuous playback without another stalling right after playback continued. + * - 'aggressive': The player will try to unstall as soon as some media data became available and will start playback of this media immediately. + * + * @remarks Platform: iOS + */ + unstallingBehaviour?: 'relaxed' | 'aggressive'; + /** + * Constantly aggregated and weighted bandwidth samples are summed up to this weight limit to calculate an bandwidth estimation. Remaining samples (i.e. that would lead to exceeding the limit) are dropped from memory as they are not relevant anymore. + * Default is 2000. + * + * @remarks Platform: Android + */ + bandwidthEstimateWeightLimit?: number; + /** + * Some devices have an incorrect implementation of MediaCodec.setOutputSurface. This leads to failure when the surface changes. To prevent failure, the codec will be released and re-instantiated in those scenarios. + * + * @remarks Platform: Android + */ + devicesThatRequireSurfaceWorkaround?: { + /** + * A device name as reported by Build.DEVICE. + * + * @see Build.DEVICE: https://developer.android.com/reference/kotlin/android/os/Build.html#DEVICE-- + */ + deviceNames?: string[]; + /** + * A model name as reported by Build.MODEL. + * + * @see Build.MODEL: https://developer.android.com/reference/kotlin/android/os/Build.html#MODEL-- + */ + modelNames?: string[]; + }; + /** + * Specifies if the language property on DASH Representations, HLS Renditions and SmoothStreaming QualityLevels is normalized. + * If enabled, language properties are normalized to IETF BCP 47 language tags. Default is true. + * + * Examples: + * - "ENG" is normalized to "en" + * - "en_us" is normalized to "en-us" + * - "en-US-x-lvariant-POSIX" is normalized to "en-us-posix" + * + * @remarks Platform: Android + */ + languagePropertyNormalization?: boolean; + /** + * The interval in which dynamic DASH windows are updated locally. I.e. The rate by which the + * playback window is moved forward on the timeline. + * + * @remarks Platform: Android + */ + localDynamicDashWindowUpdateInterval?: number; + /** + * Specifies whether a DRM session should be used for clear tracks of type video and audio. Using + * DRM sessions for clear content avoids the recreation of decoders when transitioning between clear + * and encrypted sections of content. Default is false. + * + * @remarks Platform: Android + */ + useDrmSessionForClearPeriods?: boolean; + /** + * Specifies whether a DRM session should be used for clear tracks of type video and audio in a clear + * source that follows after a DRM protected source. In addition, a DRM session will be used for clear + * periods in a DRM protected source. Using DRM sessions for clear content avoids the recreation of + * decoders when transitioning between clear and encrypted sections of content. Default is false. + * + * @remarks Platform: Android + */ + useDrmSessionForClearSources?: boolean; + /** + * Specifies if the player should always fall back to an extractor matching the file type, if no + * matching extractor was found. If the fallback is applied, this will ignore potential incompatibilities + * with streams and thus can result in unstable or failing playback. + * + * @remarks Platform: Android + */ + useFiletypeExtractorFallbackForHls?: boolean; + /** + * Determines whether `AVKit` should update Now Playing information automatically when using System UI. + * + * - If set to `false`, the automatic updates of Now Playing Info sent by `AVKit` are disabled. + * This prevents interference with manual updates you may want to perform. + * - If set to `true`, the default behaviour is maintained, allowing `AVKit` to handle Now Playing updates. + * + * Default is `true`. + * + * @deprecated To enable the Now Playing information use {@link MediaControlConfig.isEnabled} + * @remarks Platform: iOS + */ + updatesNowPlayingInfoCenter?: boolean; + /** + * When switching between video formats (eg: adapting between video qualities) + * the codec might be recreated due to several reasons. + * This behaviour can cause brief black screens when switching between video qualities as codec recreation can be + * slow. + * + * If a device is know to support video format changes and keep the current decoder without issues, + * this set can be filled with multiple `ForceReuseVideoCodecReason` and avoid the black screen. + * + * Default is `null` i.e not set + * + * @remarks Platform: Android + */ + forceReuseVideoCodecReasons?: ForceReuseVideoCodecReason[]; +} +//# sourceMappingURL=tweaksConfig.d.ts.map \ No newline at end of file diff --git a/build/tweaksConfig.d.ts.map b/build/tweaksConfig.d.ts.map new file mode 100644 index 00000000..7a1f6320 --- /dev/null +++ b/build/tweaksConfig.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"tweaksConfig.d.ts","sourceRoot":"","sources":["../src/tweaksConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,oBAAY,0BAA0B;IACpC;;OAEG;IACH,iBAAiB,sBAAsB;IACvC;;OAEG;IACH,oBAAoB,yBAAyB;IAC7C;;OAEG;IACH,qBAAqB,0BAA0B;CAChD;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;;;;;;;;OAYG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC;;;;;;;;;;;OAWG;IACH,yBAAyB,CAAC,EAAE,OAAO,CAAC;IACpC;;;;;;;;;;;;OAYG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;OAOG;IACH,sBAAsB,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;IAClD;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;IAC/C;;;;;OAKG;IACH,4BAA4B,CAAC,EAAE,MAAM,CAAC;IACtC;;;;OAIG;IACH,mCAAmC,CAAC,EAAE;QACpC;;;;WAIG;QACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB;;;;WAIG;QACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC;IACF;;;;;;;;;;OAUG;IACH,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC;;;;;OAKG;IACH,oCAAoC,CAAC,EAAE,MAAM,CAAC;IAC9C;;;;;;OAMG;IACH,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC;;;;;;;OAOG;IACH,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC;;;;;;OAMG;IACH,kCAAkC,CAAC,EAAE,OAAO,CAAC;IAC7C;;;;;;;;;;;OAWG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;IAEtC;;;;;;;;;;;;OAYG;IACH,2BAA2B,CAAC,EAAE,0BAA0B,EAAE,CAAC;CAC5D"} \ No newline at end of file diff --git a/build/tweaksConfig.js b/build/tweaksConfig.js new file mode 100644 index 00000000..cac814d3 --- /dev/null +++ b/build/tweaksConfig.js @@ -0,0 +1,25 @@ +/** + * When switching the video quality, the video decoder's configuration might change + * as the player can't always know if the codec supports such configuration change, it destroys and recreates it. + * This behaviour can cause brief black screens when switching between video qualities as codec recreation can be slow. + * + * If a codec is know to support a given configuration change without issues, + * the configuration can be added to the `TweaksConfig.forceReuseVideoCodecReasons` + * to always reuse the video codec and avoid the black screen. + */ +export var ForceReuseVideoCodecReason; +(function (ForceReuseVideoCodecReason) { + /** + * The new video quality color information is not compatible. + */ + ForceReuseVideoCodecReason["ColorInfoMismatch"] = "ColorInfoMismatch"; + /** + * The new video quality exceed the decoder's configured maximum sample size. + */ + ForceReuseVideoCodecReason["MaxInputSizeExceeded"] = "MaxInputSizeExceeded"; + /** + * The new video quality exceed the decoder's configured maximum resolution. + */ + ForceReuseVideoCodecReason["MaxResolutionExceeded"] = "MaxResolutionExceeded"; +})(ForceReuseVideoCodecReason || (ForceReuseVideoCodecReason = {})); +//# sourceMappingURL=tweaksConfig.js.map \ No newline at end of file diff --git a/build/tweaksConfig.js.map b/build/tweaksConfig.js.map new file mode 100644 index 00000000..3e86f19d --- /dev/null +++ b/build/tweaksConfig.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tweaksConfig.js","sourceRoot":"","sources":["../src/tweaksConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAN,IAAY,0BAaX;AAbD,WAAY,0BAA0B;IACpC;;OAEG;IACH,qEAAuC,CAAA;IACvC;;OAEG;IACH,2EAA6C,CAAA;IAC7C;;OAEG;IACH,6EAA+C,CAAA;AACjD,CAAC,EAbW,0BAA0B,KAA1B,0BAA0B,QAarC","sourcesContent":["/**\n * When switching the video quality, the video decoder's configuration might change\n * as the player can't always know if the codec supports such configuration change, it destroys and recreates it.\n * This behaviour can cause brief black screens when switching between video qualities as codec recreation can be slow.\n *\n * If a codec is know to support a given configuration change without issues,\n * the configuration can be added to the `TweaksConfig.forceReuseVideoCodecReasons`\n * to always reuse the video codec and avoid the black screen.\n */\nexport enum ForceReuseVideoCodecReason {\n /**\n * The new video quality color information is not compatible.\n */\n ColorInfoMismatch = 'ColorInfoMismatch',\n /**\n * The new video quality exceed the decoder's configured maximum sample size.\n */\n MaxInputSizeExceeded = 'MaxInputSizeExceeded',\n /**\n * The new video quality exceed the decoder's configured maximum resolution.\n */\n MaxResolutionExceeded = 'MaxResolutionExceeded',\n}\n\n/**\n * This configuration is used as an incubator for experimental features. Tweaks are not officially\n * supported and are not guaranteed to be stable, i.e. their naming, functionality and API can\n * change at any time within the tweaks or when being promoted to an official feature and moved\n * into its final configuration namespace.\n */\nexport interface TweaksConfig {\n /**\n * The frequency in seconds `onTimeChanged` is called with `TimeChangedEvent`s.\n *\n * Default value in iOS is `1.0`.\n * Default value in Android is `0.2`.\n *\n * @remarks Platform: iOS, Android\n */\n timeChangedInterval?: number;\n /**\n * If enabled, HLS playlists will be parsed and additional features and events are enabled. This includes:\n *\n * - MetadataEvents carrying segment-specific metadata for custom HLS tags, like `#EXT-X-SCTE35`\n * - MetadataParsedEvents carrying segment-specific metadata for custom HLS tags, like `#EXT-X-SCTE35`\n * - DrmDataParsedEvents when a `#EXT-X-KEY` is found\n * - `Player.availableVideoQualities` includes additional information\n * - Automatic retries when HLS playlist requests failed with non-2xx HTTP status code\n *\n * Default is false.\n *\n * @remarks Platform: iOS\n */\n isNativeHlsParsingEnabled?: boolean;\n /**\n * If enabled, playlists will be downloaded by the Bitmovin Player SDK instead of AVFoundation.\n * This enables additional features and events, like:\n *\n * - DownloadFinishedEvents for playlist downloads.\n * - SourceWarningEvents when no `#EXT-X-PLAYLIST-TYPE` is found If set to false, enabling\n * nativeHlsParsingEnabled won’t have any effect.\n *\n * Default is true.\n *\n * @remarks Platform: iOS\n */\n isCustomHlsLoadingEnabled?: boolean;\n /**\n * The threshold which will be applied when seeking to the end in seconds. This value will be used\n * to calculate the maximum seekable time when calling `player.seek(time:)` or `player.playlist.seek(source:time:)`,\n * so the maximum value will be duration - seekToEndThreshold.\n *\n * This is useful if the duration of the segments does not match the duration specified in the\n * manifest. In this case, if we try to seek to the end, AVPlayer could get stuck and might stall\n * forever Therefore increasing this value could help.\n *\n * Default is 0.5.\n *\n * @remarks Platform: iOS\n */\n seekToEndThreshold?: number;\n /**\n * Specifies the player behaviour when `Player.play` is called. Default is 'relaxed'.\n *\n * - 'relaxed': Starts playback when enough media data is buffered and continuous playback without stalling can be ensured. If insufficient media data is buffered for playback to start, the player will act as if the buffer became empty during playback.\n * - 'aggressive': When the buffer is not empty, this setting will cause the player to start playback of available media immediately. If insufficient media data is buffered for playback to start, the player will act as if the buffer became empty during playback.\n *\n * @remarks Platform: iOS\n */\n playbackStartBehaviour?: 'relaxed' | 'aggressive';\n /**\n * Specifies the player behaviour when stalling should be exited. Default is 'relaxed'.\n *\n * - 'relaxed': The player will wait until the buffer is filled that it can, most likely, ensure continuous playback without another stalling right after playback continued.\n * - 'aggressive': The player will try to unstall as soon as some media data became available and will start playback of this media immediately.\n *\n * @remarks Platform: iOS\n */\n unstallingBehaviour?: 'relaxed' | 'aggressive';\n /**\n * Constantly aggregated and weighted bandwidth samples are summed up to this weight limit to calculate an bandwidth estimation. Remaining samples (i.e. that would lead to exceeding the limit) are dropped from memory as they are not relevant anymore.\n * Default is 2000.\n *\n * @remarks Platform: Android\n */\n bandwidthEstimateWeightLimit?: number;\n /**\n * Some devices have an incorrect implementation of MediaCodec.setOutputSurface. This leads to failure when the surface changes. To prevent failure, the codec will be released and re-instantiated in those scenarios.\n *\n * @remarks Platform: Android\n */\n devicesThatRequireSurfaceWorkaround?: {\n /**\n * A device name as reported by Build.DEVICE.\n *\n * @see Build.DEVICE: https://developer.android.com/reference/kotlin/android/os/Build.html#DEVICE--\n */\n deviceNames?: string[];\n /**\n * A model name as reported by Build.MODEL.\n *\n * @see Build.MODEL: https://developer.android.com/reference/kotlin/android/os/Build.html#MODEL--\n */\n modelNames?: string[];\n };\n /**\n * Specifies if the language property on DASH Representations, HLS Renditions and SmoothStreaming QualityLevels is normalized.\n * If enabled, language properties are normalized to IETF BCP 47 language tags. Default is true.\n *\n * Examples:\n * - \"ENG\" is normalized to \"en\"\n * - \"en_us\" is normalized to \"en-us\"\n * - \"en-US-x-lvariant-POSIX\" is normalized to \"en-us-posix\"\n *\n * @remarks Platform: Android\n */\n languagePropertyNormalization?: boolean;\n /**\n * The interval in which dynamic DASH windows are updated locally. I.e. The rate by which the\n * playback window is moved forward on the timeline.\n *\n * @remarks Platform: Android\n */\n localDynamicDashWindowUpdateInterval?: number;\n /**\n * Specifies whether a DRM session should be used for clear tracks of type video and audio. Using\n * DRM sessions for clear content avoids the recreation of decoders when transitioning between clear\n * and encrypted sections of content. Default is false.\n *\n * @remarks Platform: Android\n */\n useDrmSessionForClearPeriods?: boolean;\n /**\n * Specifies whether a DRM session should be used for clear tracks of type video and audio in a clear\n * source that follows after a DRM protected source. In addition, a DRM session will be used for clear\n * periods in a DRM protected source. Using DRM sessions for clear content avoids the recreation of\n * decoders when transitioning between clear and encrypted sections of content. Default is false.\n *\n * @remarks Platform: Android\n */\n useDrmSessionForClearSources?: boolean;\n /**\n * Specifies if the player should always fall back to an extractor matching the file type, if no\n * matching extractor was found. If the fallback is applied, this will ignore potential incompatibilities\n * with streams and thus can result in unstable or failing playback.\n *\n * @remarks Platform: Android\n */\n useFiletypeExtractorFallbackForHls?: boolean;\n /**\n * Determines whether `AVKit` should update Now Playing information automatically when using System UI.\n *\n * - If set to `false`, the automatic updates of Now Playing Info sent by `AVKit` are disabled.\n * This prevents interference with manual updates you may want to perform.\n * - If set to `true`, the default behaviour is maintained, allowing `AVKit` to handle Now Playing updates.\n *\n * Default is `true`.\n *\n * @deprecated To enable the Now Playing information use {@link MediaControlConfig.isEnabled}\n * @remarks Platform: iOS\n */\n updatesNowPlayingInfoCenter?: boolean;\n\n /**\n * When switching between video formats (eg: adapting between video qualities)\n * the codec might be recreated due to several reasons.\n * This behaviour can cause brief black screens when switching between video qualities as codec recreation can be\n * slow.\n *\n * If a device is know to support video format changes and keep the current decoder without issues,\n * this set can be filled with multiple `ForceReuseVideoCodecReason` and avoid the black screen.\n *\n * Default is `null` i.e not set\n *\n * @remarks Platform: Android\n */\n forceReuseVideoCodecReasons?: ForceReuseVideoCodecReason[];\n}\n"]} \ No newline at end of file diff --git a/build/ui/customMessageHandlerModule.d.ts b/build/ui/customMessageHandlerModule.d.ts new file mode 100644 index 00000000..16faa40f --- /dev/null +++ b/build/ui/customMessageHandlerModule.d.ts @@ -0,0 +1,27 @@ +import { NativeModule } from 'expo-modules-core'; +export type CustomMessageHandlerModuleEvents = { + onReceivedSynchronousMessage: ({ nativeId, id, message, data, }: { + nativeId: string; + id: number; + message: string; + data: string | undefined; + }) => void; + onReceivedAsynchronousMessage: ({ nativeId, message, data, }: { + nativeId: string; + message: string; + data: string | undefined; + }) => void; +}; +/** + * Native CustomMessageHandlerModule using Expo modules API. + * Provides modern async/await interface while maintaining backward compatibility. + */ +declare class CustomMessageHandlerModule extends NativeModule { + registerHandler(nativeId: string): Promise; + destroy(nativeId: string): Promise; + onReceivedSynchronousMessageResult(id: number, result: string | undefined): Promise; + sendMessage(nativeId: string, message: string, data: string | undefined): Promise; +} +declare const _default: CustomMessageHandlerModule; +export default _default; +//# sourceMappingURL=customMessageHandlerModule.d.ts.map \ No newline at end of file diff --git a/build/ui/customMessageHandlerModule.d.ts.map b/build/ui/customMessageHandlerModule.d.ts.map new file mode 100644 index 00000000..ed7e49ba --- /dev/null +++ b/build/ui/customMessageHandlerModule.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"customMessageHandlerModule.d.ts","sourceRoot":"","sources":["../../src/ui/customMessageHandlerModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAEtE,MAAM,MAAM,gCAAgC,GAAG;IAC7C,4BAA4B,EAAE,CAAC,EAC7B,QAAQ,EACR,EAAE,EACF,OAAO,EACP,IAAI,GACL,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;KAC1B,KAAK,IAAI,CAAC;IACX,6BAA6B,EAAE,CAAC,EAC9B,QAAQ,EACR,OAAO,EACP,IAAI,GACL,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;KAC1B,KAAK,IAAI,CAAC;CACZ,CAAC;AAEF;;;GAGG;AACH,OAAO,OAAO,0BAA2B,SAAQ,YAAY,CAAC,gCAAgC,CAAC;IAC7F,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAChD,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IACxC,kCAAkC,CAChC,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB,OAAO,CAAC,IAAI,CAAC;IAChB,WAAW,CACT,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,OAAO,CAAC,IAAI,CAAC;CACjB;;AAED,wBAEE"} \ No newline at end of file diff --git a/build/ui/customMessageHandlerModule.js b/build/ui/customMessageHandlerModule.js new file mode 100644 index 00000000..9bd6b054 --- /dev/null +++ b/build/ui/customMessageHandlerModule.js @@ -0,0 +1,3 @@ +import { requireNativeModule } from 'expo-modules-core'; +export default requireNativeModule('CustomMessageHandlerModule'); +//# sourceMappingURL=customMessageHandlerModule.js.map \ No newline at end of file diff --git a/build/ui/customMessageHandlerModule.js.map b/build/ui/customMessageHandlerModule.js.map new file mode 100644 index 00000000..8898e947 --- /dev/null +++ b/build/ui/customMessageHandlerModule.js.map @@ -0,0 +1 @@ +{"version":3,"file":"customMessageHandlerModule.js","sourceRoot":"","sources":["../../src/ui/customMessageHandlerModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AA2CtE,eAAe,mBAAmB,CAChC,4BAA4B,CAC7B,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo-modules-core';\n\nexport type CustomMessageHandlerModuleEvents = {\n onReceivedSynchronousMessage: ({\n nativeId,\n id,\n message,\n data,\n }: {\n nativeId: string;\n id: number;\n message: string;\n data: string | undefined;\n }) => void;\n onReceivedAsynchronousMessage: ({\n nativeId,\n message,\n data,\n }: {\n nativeId: string;\n message: string;\n data: string | undefined;\n }) => void;\n};\n\n/**\n * Native CustomMessageHandlerModule using Expo modules API.\n * Provides modern async/await interface while maintaining backward compatibility.\n */\ndeclare class CustomMessageHandlerModule extends NativeModule {\n registerHandler(nativeId: string): Promise;\n destroy(nativeId: string): Promise;\n onReceivedSynchronousMessageResult(\n id: number,\n result: string | undefined\n ): Promise;\n sendMessage(\n nativeId: string,\n message: string,\n data: string | undefined\n ): Promise;\n}\n\nexport default requireNativeModule(\n 'CustomMessageHandlerModule'\n);\n"]} \ No newline at end of file diff --git a/build/ui/custommessagehandler.d.ts b/build/ui/custommessagehandler.d.ts new file mode 100644 index 00000000..ff16f853 --- /dev/null +++ b/build/ui/custommessagehandler.d.ts @@ -0,0 +1,56 @@ +import { CustomMessageSender } from './custommessagesender'; +export interface CustomMessageHandlerProps { + /** + * A function that will be called when the Player UI sends a synchronous message to the integration. + */ + onReceivedSynchronousMessage: (message: string, data: string | undefined) => string | undefined; + /** + * A function that will be called when the Player UI sends an asynchronous message to the integration. + */ + onReceivedAsynchronousMessage: (message: string, data: string | undefined) => void; +} +/** + * Android and iOS only. + * For Android it requires Player SDK version 3.39.0 or higher. + * + * Provides a two-way communication channel between the Player UI and the integration. + */ +export declare class CustomMessageHandler { + private readonly onReceivedSynchronousMessage; + private readonly onReceivedAsynchronousMessage; + /** @internal */ + customMessageSender?: CustomMessageSender; + /** + * Android and iOS only. + * + * Creates a new `CustomMessageHandler` instance to handle two-way communication between the integation and the Player UI. + * + * @param options - Configuration options for the `CustomMessageHandler` instance. + */ + constructor({ onReceivedSynchronousMessage, onReceivedAsynchronousMessage, }: CustomMessageHandlerProps); + /** + * Gets called when a synchronous message was received from the Bitmovin Web UI. + * + * @param message Identifier of the message. + * @param data Optional data of the message as string (can be a serialized object). + * @returns Optional return value as string which will be propagates back to the JS counterpart. + */ + receivedSynchronousMessage(message: string, data: string | undefined): string | undefined; + /** + * Gets called when an asynchronous message was received from the Bitmovin Web UI. + * + * @param message Identifier of the message. + * @param data Optional data of the message as string (can be a serialized object). + */ + receivedAsynchronousMessage(message: string, data: string | undefined): void; + /** + * Android and iOS only. + * + * Sends a message to the Player UI. + * + * @param message - Identifier for the callback which should be called. + * @param data - Payload for the callback. + */ + sendMessage(message: string, data: string | undefined): void; +} +//# sourceMappingURL=custommessagehandler.d.ts.map \ No newline at end of file diff --git a/build/ui/custommessagehandler.d.ts.map b/build/ui/custommessagehandler.d.ts.map new file mode 100644 index 00000000..2efef81b --- /dev/null +++ b/build/ui/custommessagehandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"custommessagehandler.d.ts","sourceRoot":"","sources":["../../src/ui/custommessagehandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,WAAW,yBAAyB;IACxC;;OAEG;IACH,4BAA4B,EAAE,CAC5B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GAAG,SAAS,KACrB,MAAM,GAAG,SAAS,CAAC;IACxB;;OAEG;IACH,6BAA6B,EAAE,CAC7B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GAAG,SAAS,KACrB,IAAI,CAAC;CACX;AAED;;;;;GAKG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,4BAA4B,CAGrB;IACxB,OAAO,CAAC,QAAQ,CAAC,6BAA6B,CAGpC;IAEV,gBAAgB;IAChB,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAE1C;;;;;;OAMG;gBACS,EACV,4BAA4B,EAC5B,6BAA6B,GAC9B,EAAE,yBAAyB;IAK5B;;;;;;OAMG;IACH,0BAA0B,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,MAAM,GAAG,SAAS;IAIrB;;;;;OAKG;IACH,2BAA2B,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAI5E;;;;;;;OAOG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;CAG7D"} \ No newline at end of file diff --git a/build/ui/custommessagehandler.js b/build/ui/custommessagehandler.js new file mode 100644 index 00000000..895ca737 --- /dev/null +++ b/build/ui/custommessagehandler.js @@ -0,0 +1,54 @@ +/** + * Android and iOS only. + * For Android it requires Player SDK version 3.39.0 or higher. + * + * Provides a two-way communication channel between the Player UI and the integration. + */ +export class CustomMessageHandler { + onReceivedSynchronousMessage; + onReceivedAsynchronousMessage; + /** @internal */ + customMessageSender; + /** + * Android and iOS only. + * + * Creates a new `CustomMessageHandler` instance to handle two-way communication between the integation and the Player UI. + * + * @param options - Configuration options for the `CustomMessageHandler` instance. + */ + constructor({ onReceivedSynchronousMessage, onReceivedAsynchronousMessage, }) { + this.onReceivedSynchronousMessage = onReceivedSynchronousMessage; + this.onReceivedAsynchronousMessage = onReceivedAsynchronousMessage; + } + /** + * Gets called when a synchronous message was received from the Bitmovin Web UI. + * + * @param message Identifier of the message. + * @param data Optional data of the message as string (can be a serialized object). + * @returns Optional return value as string which will be propagates back to the JS counterpart. + */ + receivedSynchronousMessage(message, data) { + return this.onReceivedSynchronousMessage(message, data); + } + /** + * Gets called when an asynchronous message was received from the Bitmovin Web UI. + * + * @param message Identifier of the message. + * @param data Optional data of the message as string (can be a serialized object). + */ + receivedAsynchronousMessage(message, data) { + this.onReceivedAsynchronousMessage(message, data); + } + /** + * Android and iOS only. + * + * Sends a message to the Player UI. + * + * @param message - Identifier for the callback which should be called. + * @param data - Payload for the callback. + */ + sendMessage(message, data) { + this.customMessageSender?.sendMessage(message, data); + } +} +//# sourceMappingURL=custommessagehandler.js.map \ No newline at end of file diff --git a/build/ui/custommessagehandler.js.map b/build/ui/custommessagehandler.js.map new file mode 100644 index 00000000..7750363d --- /dev/null +++ b/build/ui/custommessagehandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"custommessagehandler.js","sourceRoot":"","sources":["../../src/ui/custommessagehandler.ts"],"names":[],"mappings":"AAmBA;;;;;GAKG;AACH,MAAM,OAAO,oBAAoB;IACd,4BAA4B,CAGrB;IACP,6BAA6B,CAGpC;IAEV,gBAAgB;IAChB,mBAAmB,CAAuB;IAE1C;;;;;;OAMG;IACH,YAAY,EACV,4BAA4B,EAC5B,6BAA6B,GACH;QAC1B,IAAI,CAAC,4BAA4B,GAAG,4BAA4B,CAAC;QACjE,IAAI,CAAC,6BAA6B,GAAG,6BAA6B,CAAC;IACrE,CAAC;IAED;;;;;;OAMG;IACH,0BAA0B,CACxB,OAAe,EACf,IAAwB;QAExB,OAAO,IAAI,CAAC,4BAA4B,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC1D,CAAC;IAED;;;;;OAKG;IACH,2BAA2B,CAAC,OAAe,EAAE,IAAwB;QACnE,IAAI,CAAC,6BAA6B,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACpD,CAAC;IAED;;;;;;;OAOG;IACH,WAAW,CAAC,OAAe,EAAE,IAAwB;QACnD,IAAI,CAAC,mBAAmB,EAAE,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACvD,CAAC;CACF","sourcesContent":["import { CustomMessageSender } from './custommessagesender';\n\nexport interface CustomMessageHandlerProps {\n /**\n * A function that will be called when the Player UI sends a synchronous message to the integration.\n */\n onReceivedSynchronousMessage: (\n message: string,\n data: string | undefined\n ) => string | undefined;\n /**\n * A function that will be called when the Player UI sends an asynchronous message to the integration.\n */\n onReceivedAsynchronousMessage: (\n message: string,\n data: string | undefined\n ) => void;\n}\n\n/**\n * Android and iOS only.\n * For Android it requires Player SDK version 3.39.0 or higher.\n *\n * Provides a two-way communication channel between the Player UI and the integration.\n */\nexport class CustomMessageHandler {\n private readonly onReceivedSynchronousMessage: (\n message: string,\n data: string | undefined\n ) => string | undefined;\n private readonly onReceivedAsynchronousMessage: (\n message: string,\n data: string | undefined\n ) => void;\n\n /** @internal */\n customMessageSender?: CustomMessageSender;\n\n /**\n * Android and iOS only.\n *\n * Creates a new `CustomMessageHandler` instance to handle two-way communication between the integation and the Player UI.\n *\n * @param options - Configuration options for the `CustomMessageHandler` instance.\n */\n constructor({\n onReceivedSynchronousMessage,\n onReceivedAsynchronousMessage,\n }: CustomMessageHandlerProps) {\n this.onReceivedSynchronousMessage = onReceivedSynchronousMessage;\n this.onReceivedAsynchronousMessage = onReceivedAsynchronousMessage;\n }\n\n /**\n * Gets called when a synchronous message was received from the Bitmovin Web UI.\n *\n * @param message Identifier of the message.\n * @param data Optional data of the message as string (can be a serialized object).\n * @returns Optional return value as string which will be propagates back to the JS counterpart.\n */\n receivedSynchronousMessage(\n message: string,\n data: string | undefined\n ): string | undefined {\n return this.onReceivedSynchronousMessage(message, data);\n }\n\n /**\n * Gets called when an asynchronous message was received from the Bitmovin Web UI.\n *\n * @param message Identifier of the message.\n * @param data Optional data of the message as string (can be a serialized object).\n */\n receivedAsynchronousMessage(message: string, data: string | undefined): void {\n this.onReceivedAsynchronousMessage(message, data);\n }\n\n /**\n * Android and iOS only.\n *\n * Sends a message to the Player UI.\n *\n * @param message - Identifier for the callback which should be called.\n * @param data - Payload for the callback.\n */\n sendMessage(message: string, data: string | undefined): void {\n this.customMessageSender?.sendMessage(message, data);\n }\n}\n"]} \ No newline at end of file diff --git a/build/ui/custommessagehandlerbridge.d.ts b/build/ui/custommessagehandlerbridge.d.ts new file mode 100644 index 00000000..529d3b74 --- /dev/null +++ b/build/ui/custommessagehandlerbridge.d.ts @@ -0,0 +1,33 @@ +import { CustomMessageHandler } from './custommessagehandler'; +import { CustomMessageSender } from './custommessagesender'; +/** + * Takes care of JS/Native communication for a CustomMessageHandler. + */ +export declare class CustomMessageHandlerBridge implements CustomMessageSender { + readonly nativeId: string; + private customMessageHandler?; + private isDestroyed; + private onReceivedSynchronousMessageSubscription?; + private onReceivedAsynchronousMessageSubscription?; + constructor(nativeId?: string); + setCustomMessageHandler(customMessageHandler: CustomMessageHandler): void; + /** + * Destroys the native CustomMessageHandler + */ + destroy(): void; + /** + * Called by native code, when the UI sends a synchronous message. + * @internal + */ + private receivedSynchronousMessage; + /** + * Called by native code, when the UI sends an asynchronous message. + * @internal + */ + private receivedAsynchronousMessage; + /** + * Called by CustomMessageHandler, when sending a message to the UI. + */ + sendMessage(message: string, data: string | undefined): void; +} +//# sourceMappingURL=custommessagehandlerbridge.d.ts.map \ No newline at end of file diff --git a/build/ui/custommessagehandlerbridge.d.ts.map b/build/ui/custommessagehandlerbridge.d.ts.map new file mode 100644 index 00000000..f93d167f --- /dev/null +++ b/build/ui/custommessagehandlerbridge.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"custommessagehandlerbridge.d.ts","sourceRoot":"","sources":["../../src/ui/custommessagehandlerbridge.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAI5D;;GAEG;AACH,qBAAa,0BAA2B,YAAW,mBAAmB;IACpE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,oBAAoB,CAAC,CAAuB;IACpD,OAAO,CAAC,WAAW,CAAU;IAE7B,OAAO,CAAC,wCAAwC,CAAC,CAAoB;IACrE,OAAO,CAAC,yCAAyC,CAAC,CAAoB;gBAE1D,QAAQ,CAAC,EAAE,MAAM;IA8B7B,uBAAuB,CAAC,oBAAoB,EAAE,oBAAoB;IAKlE;;OAEG;IACH,OAAO;IAWP;;;OAGG;IACH,OAAO,CAAC,0BAA0B;IAYlC;;;OAGG;IACH,OAAO,CAAC,2BAA2B;IAQnC;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;CAG7D"} \ No newline at end of file diff --git a/build/ui/custommessagehandlerbridge.js b/build/ui/custommessagehandlerbridge.js new file mode 100644 index 00000000..1726b95d --- /dev/null +++ b/build/ui/custommessagehandlerbridge.js @@ -0,0 +1,72 @@ +import * as Crypto from 'expo-crypto'; +import CustomMessageHandlerModule from './customMessageHandlerModule'; +/** + * Takes care of JS/Native communication for a CustomMessageHandler. + */ +export class CustomMessageHandlerBridge { + nativeId; + customMessageHandler; + isDestroyed; + onReceivedSynchronousMessageSubscription; + onReceivedAsynchronousMessageSubscription; + constructor(nativeId) { + this.nativeId = nativeId ?? Crypto.randomUUID(); + this.isDestroyed = false; + // Set up event listeners for synchronous and asynchronous messages + this.onReceivedSynchronousMessageSubscription = + CustomMessageHandlerModule.addListener('onReceivedSynchronousMessage', ({ nativeId, id, message, data }) => { + if (nativeId !== this.nativeId) { + return; + } + this.receivedSynchronousMessage(id, message, data); + }); + this.onReceivedAsynchronousMessageSubscription = + CustomMessageHandlerModule.addListener('onReceivedAsynchronousMessage', ({ nativeId, message, data }) => { + if (nativeId !== this.nativeId) { + return; + } + this.receivedAsynchronousMessage(message, data); + }); + CustomMessageHandlerModule.registerHandler(this.nativeId); + } + setCustomMessageHandler(customMessageHandler) { + this.customMessageHandler = customMessageHandler; + this.customMessageHandler.customMessageSender = this; + } + /** + * Destroys the native CustomMessageHandler + */ + destroy() { + if (!this.isDestroyed) { + CustomMessageHandlerModule.destroy(this.nativeId); + this.onReceivedSynchronousMessageSubscription?.remove(); + this.onReceivedAsynchronousMessageSubscription?.remove(); + this.onReceivedSynchronousMessageSubscription = undefined; + this.onReceivedAsynchronousMessageSubscription = undefined; + this.isDestroyed = true; + } + } + /** + * Called by native code, when the UI sends a synchronous message. + * @internal + */ + receivedSynchronousMessage(id, message, data) { + const result = this.customMessageHandler?.receivedSynchronousMessage(message, data); + CustomMessageHandlerModule.onReceivedSynchronousMessageResult(id, result); + } + /** + * Called by native code, when the UI sends an asynchronous message. + * @internal + */ + receivedAsynchronousMessage(message, data) { + this.customMessageHandler?.receivedAsynchronousMessage(message, data); + } + // noinspection JSUnusedGlobalSymbols + /** + * Called by CustomMessageHandler, when sending a message to the UI. + */ + sendMessage(message, data) { + CustomMessageHandlerModule.sendMessage(this.nativeId, message, data); + } +} +//# sourceMappingURL=custommessagehandlerbridge.js.map \ No newline at end of file diff --git a/build/ui/custommessagehandlerbridge.js.map b/build/ui/custommessagehandlerbridge.js.map new file mode 100644 index 00000000..d1d3adfe --- /dev/null +++ b/build/ui/custommessagehandlerbridge.js.map @@ -0,0 +1 @@ +{"version":3,"file":"custommessagehandlerbridge.js","sourceRoot":"","sources":["../../src/ui/custommessagehandlerbridge.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,0BAA0B,MAAM,8BAA8B,CAAC;AAEtE;;GAEG;AACH,MAAM,OAAO,0BAA0B;IAC5B,QAAQ,CAAS;IAClB,oBAAoB,CAAwB;IAC5C,WAAW,CAAU;IAErB,wCAAwC,CAAqB;IAC7D,yCAAyC,CAAqB;IAEtE,YAAY,QAAiB;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QAChD,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QAEzB,mEAAmE;QACnE,IAAI,CAAC,wCAAwC;YAC3C,0BAA0B,CAAC,WAAW,CACpC,8BAA8B,EAC9B,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE;gBAClC,IAAI,QAAQ,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAC/B,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC,0BAA0B,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;YACrD,CAAC,CACF,CAAC;QAEJ,IAAI,CAAC,yCAAyC;YAC5C,0BAA0B,CAAC,WAAW,CACpC,+BAA+B,EAC/B,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE;gBAC9B,IAAI,QAAQ,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAC/B,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC,2BAA2B,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAClD,CAAC,CACF,CAAC;QAEJ,0BAA0B,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC5D,CAAC;IAED,uBAAuB,CAAC,oBAA0C;QAChE,IAAI,CAAC,oBAAoB,GAAG,oBAAoB,CAAC;QACjD,IAAI,CAAC,oBAAoB,CAAC,mBAAmB,GAAG,IAAI,CAAC;IACvD,CAAC;IAED;;OAEG;IACH,OAAO;QACL,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,0BAA0B,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAClD,IAAI,CAAC,wCAAwC,EAAE,MAAM,EAAE,CAAC;YACxD,IAAI,CAAC,yCAAyC,EAAE,MAAM,EAAE,CAAC;YACzD,IAAI,CAAC,wCAAwC,GAAG,SAAS,CAAC;YAC1D,IAAI,CAAC,yCAAyC,GAAG,SAAS,CAAC;YAC3D,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,0BAA0B,CAChC,EAAU,EACV,OAAe,EACf,IAAwB;QAExB,MAAM,MAAM,GAAG,IAAI,CAAC,oBAAoB,EAAE,0BAA0B,CAClE,OAAO,EACP,IAAI,CACL,CAAC;QACF,0BAA0B,CAAC,kCAAkC,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAC5E,CAAC;IAED;;;OAGG;IACK,2BAA2B,CACjC,OAAe,EACf,IAAwB;QAExB,IAAI,CAAC,oBAAoB,EAAE,2BAA2B,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACxE,CAAC;IAED,qCAAqC;IACrC;;OAEG;IACH,WAAW,CAAC,OAAe,EAAE,IAAwB;QACnD,0BAA0B,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACvE,CAAC;CACF","sourcesContent":["import { EventSubscription } from 'expo-modules-core';\nimport { CustomMessageHandler } from './custommessagehandler';\nimport { CustomMessageSender } from './custommessagesender';\nimport * as Crypto from 'expo-crypto';\nimport CustomMessageHandlerModule from './customMessageHandlerModule';\n\n/**\n * Takes care of JS/Native communication for a CustomMessageHandler.\n */\nexport class CustomMessageHandlerBridge implements CustomMessageSender {\n readonly nativeId: string;\n private customMessageHandler?: CustomMessageHandler;\n private isDestroyed: boolean;\n\n private onReceivedSynchronousMessageSubscription?: EventSubscription;\n private onReceivedAsynchronousMessageSubscription?: EventSubscription;\n\n constructor(nativeId?: string) {\n this.nativeId = nativeId ?? Crypto.randomUUID();\n this.isDestroyed = false;\n\n // Set up event listeners for synchronous and asynchronous messages\n this.onReceivedSynchronousMessageSubscription =\n CustomMessageHandlerModule.addListener(\n 'onReceivedSynchronousMessage',\n ({ nativeId, id, message, data }) => {\n if (nativeId !== this.nativeId) {\n return;\n }\n this.receivedSynchronousMessage(id, message, data);\n }\n );\n\n this.onReceivedAsynchronousMessageSubscription =\n CustomMessageHandlerModule.addListener(\n 'onReceivedAsynchronousMessage',\n ({ nativeId, message, data }) => {\n if (nativeId !== this.nativeId) {\n return;\n }\n this.receivedAsynchronousMessage(message, data);\n }\n );\n\n CustomMessageHandlerModule.registerHandler(this.nativeId);\n }\n\n setCustomMessageHandler(customMessageHandler: CustomMessageHandler) {\n this.customMessageHandler = customMessageHandler;\n this.customMessageHandler.customMessageSender = this;\n }\n\n /**\n * Destroys the native CustomMessageHandler\n */\n destroy() {\n if (!this.isDestroyed) {\n CustomMessageHandlerModule.destroy(this.nativeId);\n this.onReceivedSynchronousMessageSubscription?.remove();\n this.onReceivedAsynchronousMessageSubscription?.remove();\n this.onReceivedSynchronousMessageSubscription = undefined;\n this.onReceivedAsynchronousMessageSubscription = undefined;\n this.isDestroyed = true;\n }\n }\n\n /**\n * Called by native code, when the UI sends a synchronous message.\n * @internal\n */\n private receivedSynchronousMessage(\n id: number,\n message: string,\n data: string | undefined\n ): void {\n const result = this.customMessageHandler?.receivedSynchronousMessage(\n message,\n data\n );\n CustomMessageHandlerModule.onReceivedSynchronousMessageResult(id, result);\n }\n\n /**\n * Called by native code, when the UI sends an asynchronous message.\n * @internal\n */\n private receivedAsynchronousMessage(\n message: string,\n data: string | undefined\n ): void {\n this.customMessageHandler?.receivedAsynchronousMessage(message, data);\n }\n\n // noinspection JSUnusedGlobalSymbols\n /**\n * Called by CustomMessageHandler, when sending a message to the UI.\n */\n sendMessage(message: string, data: string | undefined): void {\n CustomMessageHandlerModule.sendMessage(this.nativeId, message, data);\n }\n}\n"]} \ No newline at end of file diff --git a/build/ui/custommessagesender.d.ts b/build/ui/custommessagesender.d.ts new file mode 100644 index 00000000..ef61e3aa --- /dev/null +++ b/build/ui/custommessagesender.d.ts @@ -0,0 +1,5 @@ +/** @internal */ +export interface CustomMessageSender { + sendMessage(message: string, data: string | undefined): void; +} +//# sourceMappingURL=custommessagesender.d.ts.map \ No newline at end of file diff --git a/build/ui/custommessagesender.d.ts.map b/build/ui/custommessagesender.d.ts.map new file mode 100644 index 00000000..a4012355 --- /dev/null +++ b/build/ui/custommessagesender.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"custommessagesender.d.ts","sourceRoot":"","sources":["../../src/ui/custommessagesender.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,MAAM,WAAW,mBAAmB;IAClC,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;CAC9D"} \ No newline at end of file diff --git a/build/ui/custommessagesender.js b/build/ui/custommessagesender.js new file mode 100644 index 00000000..22e8a865 --- /dev/null +++ b/build/ui/custommessagesender.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=custommessagesender.js.map \ No newline at end of file diff --git a/build/ui/custommessagesender.js.map b/build/ui/custommessagesender.js.map new file mode 100644 index 00000000..9bb8fe88 --- /dev/null +++ b/build/ui/custommessagesender.js.map @@ -0,0 +1 @@ +{"version":3,"file":"custommessagesender.js","sourceRoot":"","sources":["../../src/ui/custommessagesender.ts"],"names":[],"mappings":"","sourcesContent":["/** @internal */\nexport interface CustomMessageSender {\n sendMessage(message: string, data: string | undefined): void;\n}\n"]} \ No newline at end of file diff --git a/build/ui/fullscreenHandlerModule.d.ts b/build/ui/fullscreenHandlerModule.d.ts new file mode 100644 index 00000000..8e44c58e --- /dev/null +++ b/build/ui/fullscreenHandlerModule.d.ts @@ -0,0 +1,20 @@ +import { NativeModule } from 'expo-modules-core'; +export type FullscreenHandlerModuleEvents = { + onEnterFullscreen: ({ nativeId, id, }: { + nativeId: string; + id: number; + }) => void; + onExitFullscreen: ({ nativeId, id, }: { + nativeId: string; + id: number; + }) => void; +}; +declare class FullscreenHandlerModule extends NativeModule { + registerHandler(nativeId: string): Promise; + destroy(nativeId: string): Promise; + notifyFullscreenChanged(id: number, isFullscreenEnabled: boolean): Promise; + setIsFullscreenActive(nativeId: string, isFullscreenActive: boolean): Promise; +} +declare const _default: FullscreenHandlerModule; +export default _default; +//# sourceMappingURL=fullscreenHandlerModule.d.ts.map \ No newline at end of file diff --git a/build/ui/fullscreenHandlerModule.d.ts.map b/build/ui/fullscreenHandlerModule.d.ts.map new file mode 100644 index 00000000..caec5911 --- /dev/null +++ b/build/ui/fullscreenHandlerModule.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"fullscreenHandlerModule.d.ts","sourceRoot":"","sources":["../../src/ui/fullscreenHandlerModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,mBAAmB,CAAC;AAEtE,MAAM,MAAM,6BAA6B,GAAG;IAC1C,iBAAiB,EAAE,CAAC,EAClB,QAAQ,EACR,EAAE,GACH,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,EAAE,EAAE,MAAM,CAAC;KACZ,KAAK,IAAI,CAAC;IACX,gBAAgB,EAAE,CAAC,EACjB,QAAQ,EACR,EAAE,GACH,EAAE;QACD,QAAQ,EAAE,MAAM,CAAC;QACjB,EAAE,EAAE,MAAM,CAAC;KACZ,KAAK,IAAI,CAAC;CACZ,CAAC;AAEF,OAAO,OAAO,uBAAwB,SAAQ,YAAY,CAAC,6BAA6B,CAAC;IACvF,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAChD,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IACxC,uBAAuB,CACrB,EAAE,EAAE,MAAM,EACV,mBAAmB,EAAE,OAAO,GAC3B,OAAO,CAAC,IAAI,CAAC;IAChB,qBAAqB,CACnB,QAAQ,EAAE,MAAM,EAChB,kBAAkB,EAAE,OAAO,GAC1B,OAAO,CAAC,IAAI,CAAC;CACjB;;AAED,wBAEE"} \ No newline at end of file diff --git a/build/ui/fullscreenHandlerModule.js b/build/ui/fullscreenHandlerModule.js new file mode 100644 index 00000000..b7ffd238 --- /dev/null +++ b/build/ui/fullscreenHandlerModule.js @@ -0,0 +1,3 @@ +import { requireNativeModule } from 'expo-modules-core'; +export default requireNativeModule('FullscreenHandlerModule'); +//# sourceMappingURL=fullscreenHandlerModule.js.map \ No newline at end of file diff --git a/build/ui/fullscreenHandlerModule.js.map b/build/ui/fullscreenHandlerModule.js.map new file mode 100644 index 00000000..7278867f --- /dev/null +++ b/build/ui/fullscreenHandlerModule.js.map @@ -0,0 +1 @@ +{"version":3,"file":"fullscreenHandlerModule.js","sourceRoot":"","sources":["../../src/ui/fullscreenHandlerModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAgCtE,eAAe,mBAAmB,CAChC,yBAAyB,CAC1B,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo-modules-core';\n\nexport type FullscreenHandlerModuleEvents = {\n onEnterFullscreen: ({\n nativeId,\n id,\n }: {\n nativeId: string;\n id: number;\n }) => void;\n onExitFullscreen: ({\n nativeId,\n id,\n }: {\n nativeId: string;\n id: number;\n }) => void;\n};\n\ndeclare class FullscreenHandlerModule extends NativeModule {\n registerHandler(nativeId: string): Promise;\n destroy(nativeId: string): Promise;\n notifyFullscreenChanged(\n id: number,\n isFullscreenEnabled: boolean\n ): Promise;\n setIsFullscreenActive(\n nativeId: string,\n isFullscreenActive: boolean\n ): Promise;\n}\n\nexport default requireNativeModule(\n 'FullscreenHandlerModule'\n);\n"]} \ No newline at end of file diff --git a/build/ui/fullscreenhandler.d.ts b/build/ui/fullscreenhandler.d.ts new file mode 100644 index 00000000..1c0e05ac --- /dev/null +++ b/build/ui/fullscreenhandler.d.ts @@ -0,0 +1,18 @@ +/** + * Handles the UI state change when fullscreen should be entered or exited. + */ +export interface FullscreenHandler { + /** + * Indicates if the UI is currently in fullscreen mode + */ + isFullscreenActive: boolean; + /** + * Is called by the `PlayerView` when the UI should enter fullscreen mode. + */ + enterFullscreen(): void; + /** + * Is called by the `PlayerView` when the UI should exit fullscreen mode. + */ + exitFullscreen(): void; +} +//# sourceMappingURL=fullscreenhandler.d.ts.map \ No newline at end of file diff --git a/build/ui/fullscreenhandler.d.ts.map b/build/ui/fullscreenhandler.d.ts.map new file mode 100644 index 00000000..90a47696 --- /dev/null +++ b/build/ui/fullscreenhandler.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"fullscreenhandler.d.ts","sourceRoot":"","sources":["../../src/ui/fullscreenhandler.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,kBAAkB,EAAE,OAAO,CAAC;IAE5B;;OAEG;IACH,eAAe,IAAI,IAAI,CAAC;IAExB;;OAEG;IACH,cAAc,IAAI,IAAI,CAAC;CACxB"} \ No newline at end of file diff --git a/build/ui/fullscreenhandler.js b/build/ui/fullscreenhandler.js new file mode 100644 index 00000000..b8e7655a --- /dev/null +++ b/build/ui/fullscreenhandler.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=fullscreenhandler.js.map \ No newline at end of file diff --git a/build/ui/fullscreenhandler.js.map b/build/ui/fullscreenhandler.js.map new file mode 100644 index 00000000..665726cd --- /dev/null +++ b/build/ui/fullscreenhandler.js.map @@ -0,0 +1 @@ +{"version":3,"file":"fullscreenhandler.js","sourceRoot":"","sources":["../../src/ui/fullscreenhandler.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Handles the UI state change when fullscreen should be entered or exited.\n */\nexport interface FullscreenHandler {\n /**\n * Indicates if the UI is currently in fullscreen mode\n */\n isFullscreenActive: boolean;\n\n /**\n * Is called by the `PlayerView` when the UI should enter fullscreen mode.\n */\n enterFullscreen(): void;\n\n /**\n * Is called by the `PlayerView` when the UI should exit fullscreen mode.\n */\n exitFullscreen(): void;\n}\n"]} \ No newline at end of file diff --git a/build/ui/fullscreenhandlerbridge.d.ts b/build/ui/fullscreenhandlerbridge.d.ts new file mode 100644 index 00000000..8dd84e55 --- /dev/null +++ b/build/ui/fullscreenhandlerbridge.d.ts @@ -0,0 +1,26 @@ +import { FullscreenHandler } from './fullscreenhandler'; +/** + * Takes care of JS/Native communication for a FullscreenHandler. + */ +export declare class FullscreenHandlerBridge { + readonly nativeId: string; + fullscreenHandler?: FullscreenHandler; + isDestroyed: boolean; + private onEnterFullScreenSubscription?; + private onExitFullScreenSubscription?; + constructor(nativeId?: string); + setFullscreenHandler(fullscreenHandler: FullscreenHandler | undefined): void; + /** + * Destroys the native FullscreenHandler + */ + destroy(): void; + /** + * Called by native code, when the UI should enter fullscreen. + */ + private enterFullscreen; + /** + * Called by native code, when the UI should exit fullscreen. + */ + private exitFullscreen; +} +//# sourceMappingURL=fullscreenhandlerbridge.d.ts.map \ No newline at end of file diff --git a/build/ui/fullscreenhandlerbridge.d.ts.map b/build/ui/fullscreenhandlerbridge.d.ts.map new file mode 100644 index 00000000..9544f948 --- /dev/null +++ b/build/ui/fullscreenhandlerbridge.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"fullscreenhandlerbridge.d.ts","sourceRoot":"","sources":["../../src/ui/fullscreenhandlerbridge.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAIxD;;GAEG;AACH,qBAAa,uBAAuB;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,WAAW,EAAE,OAAO,CAAC;IAErB,OAAO,CAAC,6BAA6B,CAAC,CAAoB;IAC1D,OAAO,CAAC,4BAA4B,CAAC,CAAoB;gBAE7C,QAAQ,CAAC,EAAE,MAAM;IAyB7B,oBAAoB,CAAC,iBAAiB,EAAE,iBAAiB,GAAG,SAAS;IAcrE;;OAEG;IACH,OAAO;IAYP;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;OAEG;IACH,OAAO,CAAC,cAAc;CAOvB"} \ No newline at end of file diff --git a/build/ui/fullscreenhandlerbridge.js b/build/ui/fullscreenhandlerbridge.js new file mode 100644 index 00000000..94c30773 --- /dev/null +++ b/build/ui/fullscreenhandlerbridge.js @@ -0,0 +1,67 @@ +import * as Crypto from 'expo-crypto'; +import FullscreenHandlerModule from './fullscreenHandlerModule'; +/** + * Takes care of JS/Native communication for a FullscreenHandler. + */ +export class FullscreenHandlerBridge { + nativeId; + fullscreenHandler; + isDestroyed; + onEnterFullScreenSubscription; + onExitFullScreenSubscription; + constructor(nativeId) { + this.nativeId = nativeId ?? Crypto.randomUUID(); + this.isDestroyed = false; + this.onEnterFullScreenSubscription = FullscreenHandlerModule.addListener('onEnterFullscreen', ({ nativeId, id }) => { + if (nativeId !== this.nativeId) { + return; + } + this.enterFullscreen(id); + }); + this.onExitFullScreenSubscription = FullscreenHandlerModule.addListener('onExitFullscreen', ({ nativeId, id }) => { + if (nativeId !== this.nativeId) { + return; + } + this.exitFullscreen(id); + }); + FullscreenHandlerModule.registerHandler(this.nativeId); + } + setFullscreenHandler(fullscreenHandler) { + if (this.fullscreenHandler === fullscreenHandler) { + return; + } + this.fullscreenHandler = fullscreenHandler; + // synchronize current state from fullscreenHandler to native + FullscreenHandlerModule.setIsFullscreenActive(this.nativeId, fullscreenHandler?.isFullscreenActive ?? false); + } + /** + * Destroys the native FullscreenHandler + */ + destroy() { + if (!this.isDestroyed) { + FullscreenHandlerModule.destroy(this.nativeId); + this.onEnterFullScreenSubscription?.remove(); + this.onExitFullScreenSubscription?.remove(); + this.onEnterFullScreenSubscription = undefined; + this.onExitFullScreenSubscription = undefined; + this.isDestroyed = true; + } + } + // noinspection JSUnusedGlobalSymbols + /** + * Called by native code, when the UI should enter fullscreen. + */ + enterFullscreen(id) { + this.fullscreenHandler?.enterFullscreen(); + FullscreenHandlerModule.notifyFullscreenChanged(id, this.fullscreenHandler?.isFullscreenActive ?? false); + } + // noinspection JSUnusedGlobalSymbols + /** + * Called by native code, when the UI should exit fullscreen. + */ + exitFullscreen(id) { + this.fullscreenHandler?.exitFullscreen(); + FullscreenHandlerModule.notifyFullscreenChanged(id, this.fullscreenHandler?.isFullscreenActive ?? false); + } +} +//# sourceMappingURL=fullscreenhandlerbridge.js.map \ No newline at end of file diff --git a/build/ui/fullscreenhandlerbridge.js.map b/build/ui/fullscreenhandlerbridge.js.map new file mode 100644 index 00000000..768cc334 --- /dev/null +++ b/build/ui/fullscreenhandlerbridge.js.map @@ -0,0 +1 @@ +{"version":3,"file":"fullscreenhandlerbridge.js","sourceRoot":"","sources":["../../src/ui/fullscreenhandlerbridge.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,uBAAuB,MAAM,2BAA2B,CAAC;AAEhE;;GAEG;AACH,MAAM,OAAO,uBAAuB;IACzB,QAAQ,CAAS;IAC1B,iBAAiB,CAAqB;IACtC,WAAW,CAAU;IAEb,6BAA6B,CAAqB;IAClD,4BAA4B,CAAqB;IAEzD,YAAY,QAAiB;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QAChD,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QAEzB,IAAI,CAAC,6BAA6B,GAAG,uBAAuB,CAAC,WAAW,CACtE,mBAAmB,EACnB,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,EAAE;YACnB,IAAI,QAAQ,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC/B,OAAO;YACT,CAAC;YACD,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QAC3B,CAAC,CACF,CAAC;QACF,IAAI,CAAC,4BAA4B,GAAG,uBAAuB,CAAC,WAAW,CACrE,kBAAkB,EAClB,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,EAAE;YACnB,IAAI,QAAQ,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC/B,OAAO;YACT,CAAC;YACD,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;QAC1B,CAAC,CACF,CAAC;QACF,uBAAuB,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzD,CAAC;IAED,oBAAoB,CAAC,iBAAgD;QACnE,IAAI,IAAI,CAAC,iBAAiB,KAAK,iBAAiB,EAAE,CAAC;YACjD,OAAO;QACT,CAAC;QAED,IAAI,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QAE3C,6DAA6D;QAC7D,uBAAuB,CAAC,qBAAqB,CAC3C,IAAI,CAAC,QAAQ,EACb,iBAAiB,EAAE,kBAAkB,IAAI,KAAK,CAC/C,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,OAAO;QACL,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,uBAAuB,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC/C,IAAI,CAAC,6BAA6B,EAAE,MAAM,EAAE,CAAC;YAC7C,IAAI,CAAC,4BAA4B,EAAE,MAAM,EAAE,CAAC;YAC5C,IAAI,CAAC,6BAA6B,GAAG,SAAS,CAAC;YAC/C,IAAI,CAAC,4BAA4B,GAAG,SAAS,CAAC;YAC9C,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,qCAAqC;IACrC;;OAEG;IACK,eAAe,CAAC,EAAU;QAChC,IAAI,CAAC,iBAAiB,EAAE,eAAe,EAAE,CAAC;QAC1C,uBAAuB,CAAC,uBAAuB,CAC7C,EAAE,EACF,IAAI,CAAC,iBAAiB,EAAE,kBAAkB,IAAI,KAAK,CACpD,CAAC;IACJ,CAAC;IAED,qCAAqC;IACrC;;OAEG;IACK,cAAc,CAAC,EAAU;QAC/B,IAAI,CAAC,iBAAiB,EAAE,cAAc,EAAE,CAAC;QACzC,uBAAuB,CAAC,uBAAuB,CAC7C,EAAE,EACF,IAAI,CAAC,iBAAiB,EAAE,kBAAkB,IAAI,KAAK,CACpD,CAAC;IACJ,CAAC;CACF","sourcesContent":["import { EventSubscription } from 'expo-modules-core';\nimport { FullscreenHandler } from './fullscreenhandler';\nimport * as Crypto from 'expo-crypto';\nimport FullscreenHandlerModule from './fullscreenHandlerModule';\n\n/**\n * Takes care of JS/Native communication for a FullscreenHandler.\n */\nexport class FullscreenHandlerBridge {\n readonly nativeId: string;\n fullscreenHandler?: FullscreenHandler;\n isDestroyed: boolean;\n\n private onEnterFullScreenSubscription?: EventSubscription;\n private onExitFullScreenSubscription?: EventSubscription;\n\n constructor(nativeId?: string) {\n this.nativeId = nativeId ?? Crypto.randomUUID();\n this.isDestroyed = false;\n\n this.onEnterFullScreenSubscription = FullscreenHandlerModule.addListener(\n 'onEnterFullscreen',\n ({ nativeId, id }) => {\n if (nativeId !== this.nativeId) {\n return;\n }\n this.enterFullscreen(id);\n }\n );\n this.onExitFullScreenSubscription = FullscreenHandlerModule.addListener(\n 'onExitFullscreen',\n ({ nativeId, id }) => {\n if (nativeId !== this.nativeId) {\n return;\n }\n this.exitFullscreen(id);\n }\n );\n FullscreenHandlerModule.registerHandler(this.nativeId);\n }\n\n setFullscreenHandler(fullscreenHandler: FullscreenHandler | undefined) {\n if (this.fullscreenHandler === fullscreenHandler) {\n return;\n }\n\n this.fullscreenHandler = fullscreenHandler;\n\n // synchronize current state from fullscreenHandler to native\n FullscreenHandlerModule.setIsFullscreenActive(\n this.nativeId,\n fullscreenHandler?.isFullscreenActive ?? false\n );\n }\n\n /**\n * Destroys the native FullscreenHandler\n */\n destroy() {\n if (!this.isDestroyed) {\n FullscreenHandlerModule.destroy(this.nativeId);\n this.onEnterFullScreenSubscription?.remove();\n this.onExitFullScreenSubscription?.remove();\n this.onEnterFullScreenSubscription = undefined;\n this.onExitFullScreenSubscription = undefined;\n this.isDestroyed = true;\n }\n }\n\n // noinspection JSUnusedGlobalSymbols\n /**\n * Called by native code, when the UI should enter fullscreen.\n */\n private enterFullscreen(id: number): void {\n this.fullscreenHandler?.enterFullscreen();\n FullscreenHandlerModule.notifyFullscreenChanged(\n id,\n this.fullscreenHandler?.isFullscreenActive ?? false\n );\n }\n\n // noinspection JSUnusedGlobalSymbols\n /**\n * Called by native code, when the UI should exit fullscreen.\n */\n private exitFullscreen(id: number): void {\n this.fullscreenHandler?.exitFullscreen();\n FullscreenHandlerModule.notifyFullscreenChanged(\n id,\n this.fullscreenHandler?.isFullscreenActive ?? false\n );\n }\n}\n"]} \ No newline at end of file diff --git a/build/ui/index.d.ts b/build/ui/index.d.ts new file mode 100644 index 00000000..a36da5b7 --- /dev/null +++ b/build/ui/index.d.ts @@ -0,0 +1,3 @@ +export * from './fullscreenhandler'; +export * from './custommessagehandler'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/build/ui/index.d.ts.map b/build/ui/index.d.ts.map new file mode 100644 index 00000000..f8c8adbb --- /dev/null +++ b/build/ui/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC"} \ No newline at end of file diff --git a/build/ui/index.js b/build/ui/index.js new file mode 100644 index 00000000..72a71fe9 --- /dev/null +++ b/build/ui/index.js @@ -0,0 +1,3 @@ +export * from './fullscreenhandler'; +export * from './custommessagehandler'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/build/ui/index.js.map b/build/ui/index.js.map new file mode 100644 index 00000000..aa32f408 --- /dev/null +++ b/build/ui/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC","sourcesContent":["export * from './fullscreenhandler';\nexport * from './custommessagehandler';\n"]} \ No newline at end of file diff --git a/build/utils/normalizeNonFinite.d.ts b/build/utils/normalizeNonFinite.d.ts new file mode 100644 index 00000000..a6e1d688 --- /dev/null +++ b/build/utils/normalizeNonFinite.d.ts @@ -0,0 +1,2 @@ +export declare function normalizeNonFinite(input: T): T; +//# sourceMappingURL=normalizeNonFinite.d.ts.map \ No newline at end of file diff --git a/build/utils/normalizeNonFinite.d.ts.map b/build/utils/normalizeNonFinite.d.ts.map new file mode 100644 index 00000000..46ea3c26 --- /dev/null +++ b/build/utils/normalizeNonFinite.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"normalizeNonFinite.d.ts","sourceRoot":"","sources":["../../src/utils/normalizeNonFinite.ts"],"names":[],"mappings":"AAAA,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAejD"} \ No newline at end of file diff --git a/build/utils/normalizeNonFinite.js b/build/utils/normalizeNonFinite.js new file mode 100644 index 00000000..52ff38ae --- /dev/null +++ b/build/utils/normalizeNonFinite.js @@ -0,0 +1,22 @@ +export function normalizeNonFinite(input) { + const sentinelPrefix = 'BMP_'; + function walk(v) { + if (v === `${sentinelPrefix}Infinity`) + return Infinity; + if (v === `${sentinelPrefix}-Infinity`) + return -Infinity; + if (v === `${sentinelPrefix}NaN`) + return NaN; + if (Array.isArray(v)) + return v.map(walk); + if (v && typeof v === 'object') { + const out = {}; + for (const k of Object.keys(v)) + out[k] = walk(v[k]); + return out; + } + return v; + } + return walk(input); +} +//# sourceMappingURL=normalizeNonFinite.js.map \ No newline at end of file diff --git a/build/utils/normalizeNonFinite.js.map b/build/utils/normalizeNonFinite.js.map new file mode 100644 index 00000000..e21e9933 --- /dev/null +++ b/build/utils/normalizeNonFinite.js.map @@ -0,0 +1 @@ +{"version":3,"file":"normalizeNonFinite.js","sourceRoot":"","sources":["../../src/utils/normalizeNonFinite.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,kBAAkB,CAAI,KAAQ;IAC5C,MAAM,cAAc,GAAG,MAAM,CAAC;IAC9B,SAAS,IAAI,CAAC,CAAM;QAClB,IAAI,CAAC,KAAK,GAAG,cAAc,UAAU;YAAE,OAAO,QAAQ,CAAC;QACvD,IAAI,CAAC,KAAK,GAAG,cAAc,WAAW;YAAE,OAAO,CAAC,QAAQ,CAAC;QACzD,IAAI,CAAC,KAAK,GAAG,cAAc,KAAK;YAAE,OAAO,GAAG,CAAC;QAC7C,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC/B,MAAM,GAAG,GAAQ,EAAE,CAAC;YACpB,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;gBAAE,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAE,CAAS,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7D,OAAO,GAAG,CAAC;QACb,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;AACrB,CAAC","sourcesContent":["export function normalizeNonFinite(input: T): T {\n const sentinelPrefix = 'BMP_';\n function walk(v: any): any {\n if (v === `${sentinelPrefix}Infinity`) return Infinity;\n if (v === `${sentinelPrefix}-Infinity`) return -Infinity;\n if (v === `${sentinelPrefix}NaN`) return NaN;\n if (Array.isArray(v)) return v.map(walk);\n if (v && typeof v === 'object') {\n const out: any = {};\n for (const k of Object.keys(v)) out[k] = walk((v as any)[k]);\n return out;\n }\n return v;\n }\n return walk(input);\n}\n"]} \ No newline at end of file diff --git a/example/.env.example b/example/.env.example new file mode 100644 index 00000000..12177e5f --- /dev/null +++ b/example/.env.example @@ -0,0 +1,13 @@ +# Local development configuration for the Bitmovin Player React Native SDK example app. +# Copy this file to `example/.env` and fill in your details. + +# Bitmovin Player License Key for running the example app (required) +BITMOVIN_PLAYER_LICENSE_KEY="YOUR_LICENSE_KEY_HERE" + +# Apple Developer Team ID for signing the iOS app (optional) +# This is only needed to build the app on a physical iOS/tvOS device. +# You can find your Team ID on the Apple Developer website under "Membership details". +APPLE_DEVELOPMENT_TEAM_ID="YOUR_TEAM_ID_HERE" + +# Bitmovin Analytics License Key for running the example app (optional, only needed for the Basic Analytics example) +EXPO_PUBLIC_BITMOVIN_ANALYTICS_LICENSE_KEY="YOUR_ANALYTICS_LICENSE_KEY_HERE" diff --git a/example/.eslintrc.js b/example/.eslintrc.js new file mode 100644 index 00000000..efc4745b --- /dev/null +++ b/example/.eslintrc.js @@ -0,0 +1,11 @@ +module.exports = { + root: true, + extends: ['expo', 'prettier'], + plugins: ['prettier'], + env: { + node: true, + }, + rules: { + 'prettier/prettier': 'error', + }, +}; diff --git a/example/.gitignore b/example/.gitignore new file mode 100644 index 00000000..616da25b --- /dev/null +++ b/example/.gitignore @@ -0,0 +1,36 @@ +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +# dependencies +node_modules/ + +# Expo +.expo/ +dist/ +web-build/ + +# Native +*.orig.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision + +# Metro +.metro-health-check* + +# debug +npm-debug.* +yarn-debug.* +yarn-error.* + +# macOS +.DS_Store +*.pem + +# local env files +.env*.local + +# typescript +*.tsbuildinfo +expo.log diff --git a/example/README.md b/example/README.md index f1e64461..bd5705ce 100644 --- a/example/README.md +++ b/example/README.md @@ -24,46 +24,51 @@ To play back a custom video asset, it is possible to set up a simple playback se ## Getting started -To get started with the project, run `yarn bootstrap` in the library's root directory (not `example/`). This will install dependencies for both the library and the example application (as well as native dependencies): +To get started with the project, run `yarn bootstrap` in the library's root directory (not `example/`). This command handles all setup steps automatically, including installing dependencies for both the library and the example application, generating native projects, and installing native dependencies: ```sh cd bitmovin-player-react-native # Go to library's root directory -yarn bootstrap # Install all dependencies +yarn bootstrap # Handles all setup: installs deps, prebuilds native projects, installs pods ``` -## Configuring your license key +The `yarn bootstrap` command executes the following steps: -Before running the application, make sure to set up your Bitmovin's license key in the native metadata file of each platform: +1. Installs root project dependencies +2. Installs example app dependencies +3. Generates native iOS and Android projects using Expo prebuild +4. Installs CocoaPods dependencies for iOS (macOS only) -**iOS** +Note that Windows users should instead run: -Edit the license key in `example/ios/BitmovinPlayerReactNativeExample/Info.plist`: - -```xml -BitmovinPlayerLicenseKey -ENTER_LICENSE_KEY +```powershell +cd bitmovin-player-react-native # Go to library's root directory +yarn install # Install root project dependencies +yarn example install # Install example folder dependencies +yarn example prebuild # Generate native projects ``` -**tvOS** +## Development Setup -Edit the license key in `example/ios/BitmovinPlayerReactNativeExample-tvOS/Info.plist`: +To run the example app for local development, you need to provide a Bitmovin Player license key. This project uses a local `.env` file and a dynamic `app.config.ts` to manage these secrets. -```xml -BitmovinPlayerLicenseKey -ENTER_LICENSE_KEY -``` +1. **Create a local environment file:** + From the root of the repository, copy the example `.env` template: -**Android** + ```bash + cp example/.env.example example/.env + ``` -Edit the license key in `example/android/app/src/main/AndroidManifest.xml`: +2. **Add your credentials:** + Open `example/.env` and replace the placeholder values. + - The `BITMOVIN_PLAYER_LICENSE_KEY` is required. + - The `BITMOVIN_ANALYTICS_LICENSE_KEY` is optional. It is used in the "Basic Analytics" screen. + - The `APPLE_DEVELOPMENT_TEAM_ID` is optional and only needed if you want to build the app on a physical iOS or tvOS device. You can find your Apple Team ID on the [Apple Developer website](https://developer.apple.com/account/) under "Membership details". -```xml - -``` +These values are loaded automatically by `example/app.config.ts` during the prebuild process and are not committed to version control. **This method is for internal development only.** -**Programmatically** +### Alternative: Programmatic License Key -Alternatively you can provide your license key programmatically via the config object of `usePlayer`. Providing it this way removes the need for the step above, but keep in mind that at least one of them is necessary to successfully run the examples. +Alternatively you can provide your license key programmatically via the config object of `usePlayer`. This method can be used alongside the `.env` configuration. ```ts const player = usePlayer({ @@ -74,44 +79,36 @@ const player = usePlayer({ ### Add the Package Name and Bundle Identifiers as an Allowed Domain -Add the following package names and bundle identifiers of the example applications ending as an allowed domain on [https://bitmovin.com/dashboard](https://bitmovin.com/dashboard), under `Player -> Licenses` and also under `Analytics -> Licenses`. +Add the following package name/bundle identifier `com.bitmovin.player.reactnative.example` of the example application as an allowed domain on [https://bitmovin.com/dashboard](https://bitmovin.com/dashboard), under `Player -> Licenses` and also under `Analytics -> Licenses`. -#### Android example application Package Name +### Re-generate the native iOS and Android applications -``` -com.bitmovin.player.reactnative.example -``` +When changing `example/.env` or `example/app.config.ts` you will need to re-generate the native iOS and Android applications to pick those changes up. -#### iOS example application Bundle Identifier - -``` -com.bitmovin.PlayerReactNative-Example -``` - -#### tvOS example application Bundle Identifier - -``` -com.bitmovin.PlayerReactNativeExample-tvOS -``` +The example application uses Expo Continuous Native Generation ([CNG](https://docs.expo.dev/workflow/continuous-native-generation/)) and the native apps can be re-generated using `yarn example prebuild`. +See `yarn example prebuild -h` for all options. ## Running the application **Terminal** ```sh -yarn example ios # Run examples on iOS -yarn example android # Run examples on Android -``` +# If TV examples were ran last time +yarn example prebiuld --clean -Hint: You can provide a specific simulator by name when using `--simulator` flag. `xcrun simctl list devices available` provides you with a list of available devices in your environment. +yarn example ios # Run examples on iOS simulator, see yarn example ios -h for more options +yarn example android # Run examples on Android, see yarn example android -h for more options -```sh -yarn example ios --simulator="iPhone 15 Pro" +# Before running TV examples +yarn example prebuild:tv --clean + +yarn example tvos # Run examples on tvOS simulator, see yarn example tvos -h for more options +yarn example android-tv # Run examples on Android TV, see yarn example android-tv -h for more options ``` **IDE** -You can also open the iOS project using Xcode by typing `xed example/ios` on terminal, or `studio example/android` to open the android project in Android Studio (make sure to setup its binaries first). +You can also open the iOS project using Xcode by running `yarn example open:ios` on terminal, or `yarn example open:android` to open the Android project in Android Studio. ### Running the bundler only @@ -122,3 +119,17 @@ To start the metro bundler, run the following command on the library's root (alw ```sh yarn example start # Starts bundler on the example folder ``` + +## Architecture + +This example app is built as an Expo application using: + +- **Expo SDK**: Modern React Native development with prebuild workflow +- **Bitmovin Player React Native SDK**: Directly from the root project +- **Environment Configuration**: Secure license key management via dotenv + +# Troubleshooting + +- Run `yarn example prebuild --clean`, `yarn example prebuild:tv --clean` or `yarn integration-test prebuild --clean` if you encounter native build issues +- Check `example/.env` and `integration_test/.env` file configurations for missing environment variables +- Ensure autolinking is working: parent package should be resolved automatically diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle deleted file mode 100644 index 26a1cc66..00000000 --- a/example/android/app/build.gradle +++ /dev/null @@ -1,129 +0,0 @@ -apply plugin: "com.android.application" -apply plugin: "org.jetbrains.kotlin.android" -apply plugin: "com.facebook.react" - -/** - * This is the configuration block to customize your React Native Android app. - * By default you don't need to apply any configuration, just uncomment the lines you need. - */ -react { - /* Folders */ - // The root of your project, i.e. where "package.json" lives. Default is '..' - // root = file("../") - // The folder where the react-native NPM package is. Default is ../node_modules/react-native - // reactNativeDir = file("../node_modules/react-native") - // The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen - // codegenDir = file("../node_modules/@react-native/codegen") - // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js - // cliFile = file("../node_modules/react-native/cli.js") - - /* Variants */ - // The list of variants to that are debuggable. For those we're going to - // skip the bundling of the JS bundle and the assets. By default is just 'debug'. - // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. - // debuggableVariants = ["liteDebug", "prodDebug"] - - /* Bundling */ - // A list containing the node command and its flags. Default is just 'node'. - // nodeExecutableAndArgs = ["node"] - // - // The command to run when bundling. By default is 'bundle' - // bundleCommand = "ram-bundle" - // - // The path to the CLI configuration file. Default is empty. - // bundleConfig = file(../rn-cli.config.js) - // - // The name of the generated asset file containing your JS bundle - // bundleAssetName = "MyApplication.android.bundle" - // - // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' - // entryFile = file("../js/MyApplication.android.js") - // - // A list of extra flags to pass to the 'bundle' commands. - // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle - // extraPackagerArgs = [] - - /* Hermes Commands */ - // The hermes compiler command to run. By default it is 'hermesc' - // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" - // - // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" - // hermesFlags = ["-O", "-output-source-map"] -} - -/** - * Set this to true to Run Proguard on Release builds to minify the Java bytecode. - */ -def enableProguardInReleaseBuilds = false - -/** - * The preferred build flavor of JavaScriptCore (JSC) - * - * For example, to use the international variant, you can use: - * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` - * - * The international variant includes ICU i18n library and necessary data - * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that - * give correct results when using with locales other than en-US. Note that - * this variant is about 6MiB larger per architecture than default. - */ -def jscFlavor = 'org.webkit:android-jsc:+' - -android { - ndkVersion rootProject.ext.ndkVersion - - buildToolsVersion rootProject.ext.buildToolsVersion - compileSdkVersion rootProject.ext.compileSdkVersion - - namespace "com.bitmovin.player.reactnative.example" - defaultConfig { - applicationId "com.bitmovin.player.reactnative.example" - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 1 - versionName "1.0" - } - signingConfigs { - debug { - storeFile file('debug.keystore') - storePassword 'android' - keyAlias 'androiddebugkey' - keyPassword 'android' - } - } - buildTypes { - debug { - signingConfig signingConfigs.debug - } - release { - // Caution! In production, you need to generate your own keystore file. - // see https://reactnative.dev/docs/signed-apk-android. - signingConfig signingConfigs.debug - minifyEnabled enableProguardInReleaseBuilds - proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" - } - } - buildFeatures { - buildConfig true - } -} - -dependencies { - // The version of react-native is set by the React Native Gradle Plugin - implementation("com.facebook.react:react-android") - - if (hermesEnabled.toBoolean()) { - implementation("com.facebook.react:hermes-android") - } else { - implementation jscFlavor - } - - // Only needed if the offline feature is used - implementation "androidx.localbroadcastmanager:localbroadcastmanager:1.1.0" - - // only needed if the casting feature is used - implementation("com.google.android.gms:play-services-cast-framework:21.3.0") - implementation("androidx.mediarouter:mediarouter:1.3.1") -} - -apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) diff --git a/example/android/app/debug.keystore b/example/android/app/debug.keystore deleted file mode 100644 index 364e105e..00000000 Binary files a/example/android/app/debug.keystore and /dev/null differ diff --git a/example/android/app/proguard-rules.pro b/example/android/app/proguard-rules.pro deleted file mode 100644 index 11b02572..00000000 --- a/example/android/app/proguard-rules.pro +++ /dev/null @@ -1,10 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index a4d13a32..00000000 --- a/example/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 346bfaab..00000000 --- a/example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/example/android/app/src/main/java/com/bitmovin/player/reactnative/example/MainActivity.kt b/example/android/app/src/main/java/com/bitmovin/player/reactnative/example/MainActivity.kt deleted file mode 100644 index 35aca775..00000000 --- a/example/android/app/src/main/java/com/bitmovin/player/reactnative/example/MainActivity.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.bitmovin.player.reactnative.example - -import android.os.Bundle -import com.facebook.react.ReactActivity -import com.facebook.react.ReactActivityDelegate -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled -import com.facebook.react.defaults.DefaultReactActivityDelegate -import com.google.android.gms.cast.framework.CastContext -import android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - -class MainActivity : ReactActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(null) - try { - // Load Google Cast context eagerly in order to ensure that - // the cast state is updated correctly. - CastContext.getSharedInstance(this, Runnable::run) - } catch (e: Exception) { - // cast framework not supported - } - - // Prevent going into ambient mode on Android TV devices / screen timeout on mobile devices during playback. - // If your app uses multiple activities make sure to add this flag to the activity that hosts the player. - // Reference: https://developer.android.com/training/scheduling/wakelock#screen - getWindow().addFlags(FLAG_KEEP_SCREEN_ON) - } - - /** - * Returns the name of the main component registered from JavaScript. This is used to schedule - * rendering of the component. - */ - override fun getMainComponentName(): String = "BitmovinPlayerReactNativeExample" - - /** - * Returns the instance of the [ReactActivityDelegate]. Here we use a util class [ ] which allows you to easily enable Fabric and Concurrent React - * (aka React 18) with two boolean flags. - */ - override fun createReactActivityDelegate() = DefaultReactActivityDelegate( - this, - mainComponentName, // If you opted-in for the New Architecture, we enable the Fabric Renderer. - fabricEnabled, - ) -} diff --git a/example/android/app/src/main/java/com/bitmovin/player/reactnative/example/MainApplication.kt b/example/android/app/src/main/java/com/bitmovin/player/reactnative/example/MainApplication.kt deleted file mode 100644 index c29471c3..00000000 --- a/example/android/app/src/main/java/com/bitmovin/player/reactnative/example/MainApplication.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.bitmovin.player.reactnative.example - -import android.app.Application -import com.facebook.react.PackageList -import com.facebook.react.ReactApplication -import com.facebook.react.ReactHost -import com.facebook.react.ReactNativeHost -import com.facebook.react.ReactPackage -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load -import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost -import com.facebook.react.defaults.DefaultReactNativeHost -import com.facebook.soloader.SoLoader - -class MainApplication : Application(), ReactApplication { - - override val reactNativeHost: ReactNativeHost = - object : DefaultReactNativeHost(this) { - override fun getPackages(): List = - PackageList(this).packages.apply { - // Packages that cannot be autolinked yet can be added manually here, for example: - // add(MyReactNativePackage()) - } - - override fun getJSMainModuleName(): String = "index" - - override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG - - override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED - override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED - } - - override val reactHost: ReactHost - get() = getDefaultReactHost(this.applicationContext, reactNativeHost) - - override fun onCreate() { - super.onCreate() - SoLoader.init(this, false) - if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { - // If you opted-in for the New Architecture, we load the native entry point for this app. - load() - } - } -} diff --git a/example/android/app/src/main/res/drawable/rn_edit_text_material.xml b/example/android/app/src/main/res/drawable/rn_edit_text_material.xml deleted file mode 100644 index b0680b1f..00000000 --- a/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index a2f59082..00000000 Binary files a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 1b523998..00000000 Binary files a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index ff10afd6..00000000 Binary files a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 115a4c76..00000000 Binary files a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index dcd3cd80..00000000 Binary files a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 459ca609..00000000 Binary files a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 8ca12fe0..00000000 Binary files a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 8e19b410..00000000 Binary files a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index b824ebdd..00000000 Binary files a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 4c19a13c..00000000 Binary files a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/example/android/app/src/main/res/values/strings.xml b/example/android/app/src/main/res/values/strings.xml deleted file mode 100644 index 5a61b6eb..00000000 --- a/example/android/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Bitmovin Player React Native Example - diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml deleted file mode 100644 index 7ba83a2a..00000000 --- a/example/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/example/android/build.gradle b/example/android/build.gradle deleted file mode 100644 index a70f1c5b..00000000 --- a/example/android/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - ext { - buildToolsVersion = "34.0.0" - minSdkVersion = 21 - compileSdkVersion = 34 - targetSdkVersion = 34 - - ndkVersion = "25.1.8937393" - } - repositories { - google() - mavenCentral() - } - dependencies { - classpath("com.android.tools.build:gradle") - classpath("com.facebook.react:react-native-gradle-plugin") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") - } -} - -allprojects { - repositories { - google() - mavenCentral() - maven { url 'https://artifacts.bitmovin.com/artifactory/public-releases' } - } -} -apply plugin: "com.facebook.react.rootproject" diff --git a/example/android/gradle.properties b/example/android/gradle.properties deleted file mode 100644 index a46a5b90..00000000 --- a/example/android/gradle.properties +++ /dev/null @@ -1,41 +0,0 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m -org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true - -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app's APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true -# Automatically convert third-party libraries to use AndroidX -android.enableJetifier=true - -# Use this property to specify which architecture you want to build. -# You can also override it from the CLI using -# ./gradlew -PreactNativeArchitectures=x86_64 -reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 - -# Use this property to enable support to the new architecture. -# This will allow you to use TurboModules and the Fabric render in -# your application. You should enable this flag either if you want -# to write custom TurboModules/Fabric components OR use libraries that -# are providing them. -newArchEnabled=false - -# Use this property to enable or disable the Hermes JS engine. -# If set to false, you will be using JSC instead. -hermesEnabled=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.jar b/example/android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index e708b1c0..00000000 Binary files a/example/android/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index d0d403e2..00000000 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/example/android/gradlew b/example/android/gradlew deleted file mode 100755 index 1b6c7873..00000000 --- a/example/android/gradlew +++ /dev/null @@ -1,234 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" -APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/example/android/gradlew.bat b/example/android/gradlew.bat deleted file mode 100644 index 27974359..00000000 --- a/example/android/gradlew.bat +++ /dev/null @@ -1,88 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem http://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/example/android/settings.gradle b/example/android/settings.gradle deleted file mode 100644 index afbc06b8..00000000 --- a/example/android/settings.gradle +++ /dev/null @@ -1,4 +0,0 @@ -rootProject.name = 'BitmovinPlayerReactNativeExample' -apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) -include ':app' -includeBuild('../node_modules/@react-native/gradle-plugin') diff --git a/example/app.config.ts b/example/app.config.ts new file mode 100644 index 00000000..2ed8b8f2 --- /dev/null +++ b/example/app.config.ts @@ -0,0 +1,100 @@ +import { ExpoConfig } from '@expo/config-types'; +import dotenv from 'dotenv'; +import fs from 'fs'; +import path from 'path'; + +const envPath = path.resolve(__dirname, '.env'); + +if (!fs.existsSync(envPath)) { + throw new Error( + `Environment file not found at "example/.env". Please copy "example/.env.example" to "example/.env" and fill it out.` + ); +} + +// Load environment variables from .env file +dotenv.config({ path: envPath }); + +const { BITMOVIN_PLAYER_LICENSE_KEY, APPLE_DEVELOPMENT_TEAM_ID } = process.env; + +if (!BITMOVIN_PLAYER_LICENSE_KEY) { + throw new Error( + 'BITMOVIN_PLAYER_LICENSE_KEY is not set in example/.env. Please follow the setup instructions in example/README.md.' + ); +} + +if (!APPLE_DEVELOPMENT_TEAM_ID) { + console.warn( + 'APPLE_DEVELOPMENT_TEAM_ID is not set in example/.env. This is required for running on real iOS/tvOS devices. Please follow the setup instructions in example/README.md.' + ); +} + +const config: ExpoConfig = { + name: 'Bitmovin Player React Native Example', + slug: 'bitmovin-player-react-native-example', + version: '1.0.0', + orientation: 'portrait', + icon: './assets/icon.png', + userInterfaceStyle: 'light', + splash: { + image: './assets/splash-icon.png', + resizeMode: 'contain', + backgroundColor: '#1EABE3', + }, + ios: { + supportsTablet: true, + bundleIdentifier: 'com.bitmovin.player.reactnative.example', + ...(APPLE_DEVELOPMENT_TEAM_ID && { + appleTeamId: APPLE_DEVELOPMENT_TEAM_ID, + }), + }, + android: { + adaptiveIcon: { + foregroundImage: './assets/adaptive-icon.png', + backgroundColor: '#1EABE3', + }, + package: 'com.bitmovin.player.reactnative.example', + }, + plugins: [ + [ + '@react-native-tvos/config-tv', + { + androidTVBanner: './assets/android-tv-banner.png', + appleTVImages: { + icon: './assets/tvos-1280x768.png', + iconSmall: './assets/icon-tvos.png', + iconSmall2x: './assets/icon-tvos@2x.png', + topShelf: './assets/TopShelf.png', + topShelf2x: './assets/TopShelf@2x.png', + topShelfWide: './assets/TopShelfWide.png', + topShelfWide2x: './assets/TopShelfWide@2x.png', + }, + }, + ], + [ + 'expo-build-properties', + { + android: { + buildToolsVersion: '35.0.0', + }, + ios: { + flipper: false, + }, + }, + ], + [ + '../app.plugin.js', + { + playerLicenseKey: BITMOVIN_PLAYER_LICENSE_KEY, + features: { + airPlay: true, + backgroundPlayback: true, + googleCastSDK: { android: '21.3.0', ios: '4.8.1.2' }, + offline: true, + pictureInPicture: true, + }, + }, + ], + ], +}; + +export default config; diff --git a/example/app.json b/example/app.json deleted file mode 100644 index c4c57f4a..00000000 --- a/example/app.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "BitmovinPlayerReactNativeExample", - "displayName": "Bitmovin Player React Native Example" -} diff --git a/example/assets/TopShelf.png b/example/assets/TopShelf.png new file mode 100644 index 00000000..24075fd4 Binary files /dev/null and b/example/assets/TopShelf.png differ diff --git a/example/assets/TopShelf@2x.png b/example/assets/TopShelf@2x.png new file mode 100644 index 00000000..42652eb1 Binary files /dev/null and b/example/assets/TopShelf@2x.png differ diff --git a/example/assets/TopShelfWide.png b/example/assets/TopShelfWide.png new file mode 100644 index 00000000..0e4e4c60 Binary files /dev/null and b/example/assets/TopShelfWide.png differ diff --git a/example/assets/TopShelfWide@2x.png b/example/assets/TopShelfWide@2x.png new file mode 100644 index 00000000..e969bd4b Binary files /dev/null and b/example/assets/TopShelfWide@2x.png differ diff --git a/example/assets/adaptive-icon.png b/example/assets/adaptive-icon.png new file mode 100644 index 00000000..9397a16e Binary files /dev/null and b/example/assets/adaptive-icon.png differ diff --git a/example/assets/android-tv-banner.png b/example/assets/android-tv-banner.png new file mode 100644 index 00000000..4e908fbf Binary files /dev/null and b/example/assets/android-tv-banner.png differ diff --git a/example/assets/icon-tvos.png b/example/assets/icon-tvos.png new file mode 100644 index 00000000..4d1bdfae Binary files /dev/null and b/example/assets/icon-tvos.png differ diff --git a/example/assets/icon-tvos@2x.png b/example/assets/icon-tvos@2x.png new file mode 100644 index 00000000..9bd847a9 Binary files /dev/null and b/example/assets/icon-tvos@2x.png differ diff --git a/example/assets/icon.png b/example/assets/icon.png new file mode 100644 index 00000000..9397a16e Binary files /dev/null and b/example/assets/icon.png differ diff --git a/example/assets/splash-icon.png b/example/assets/splash-icon.png new file mode 100644 index 00000000..9397a16e Binary files /dev/null and b/example/assets/splash-icon.png differ diff --git a/example/assets/tvos-1280x768.png b/example/assets/tvos-1280x768.png new file mode 100644 index 00000000..a8a89c81 Binary files /dev/null and b/example/assets/tvos-1280x768.png differ diff --git a/example/babel.config.js b/example/babel.config.js index 50094525..9d89e131 100644 --- a/example/babel.config.js +++ b/example/babel.config.js @@ -1,17 +1,6 @@ -const path = require('path'); -const pak = require('../package.json'); - -module.exports = { - presets: ['@react-native/babel-preset'], - plugins: [ - [ - 'module-resolver', - { - extensions: ['.tsx', '.ts', '.js', '.json'], - alias: { - [pak.name]: path.join(__dirname, '../src/'), - }, - }, - ], - ], +module.exports = function (api) { + api.cache(true); + return { + presets: ['babel-preset-expo'], + }; }; diff --git a/example/index.ts b/example/index.ts new file mode 100644 index 00000000..018d06f9 --- /dev/null +++ b/example/index.ts @@ -0,0 +1,8 @@ +import { registerRootComponent } from 'expo'; + +import App from './src/App'; + +// registerRootComponent calls AppRegistry.registerComponent('main', () => App); +// It also ensures that whether you load the app in Expo Go or in a native build, +// the environment is set up appropriately +registerRootComponent(App); diff --git a/example/index.tsx b/example/index.tsx deleted file mode 100644 index 117ddcae..00000000 --- a/example/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { AppRegistry } from 'react-native'; -import App from './src/App'; -import { name as appName } from './app.json'; - -AppRegistry.registerComponent(appName, () => App); diff --git a/example/ios/.xcode.env b/example/ios/.xcode.env deleted file mode 100644 index 3d5782c7..00000000 --- a/example/ios/.xcode.env +++ /dev/null @@ -1,11 +0,0 @@ -# This `.xcode.env` file is versioned and is used to source the environment -# used when running script phases inside Xcode. -# To customize your local environment, you can create an `.xcode.env.local` -# file that is not versioned. - -# NODE_BINARY variable contains the PATH to the node executable. -# -# Customize the NODE_BINARY variable here. -# For example, to use nvm with brew, add the following line -# . "$(brew --prefix nvm)/nvm.sh" --no-use -export NODE_BINARY=$(command -v node) diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/AppDelegate.h b/example/ios/BitmovinPlayerReactNativeExample-tvOS/AppDelegate.h deleted file mode 100644 index ac490f9b..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/AppDelegate.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import -#import - -@interface AppDelegate : RCTAppDelegate - -@end diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/AppDelegate.mm b/example/ios/BitmovinPlayerReactNativeExample-tvOS/AppDelegate.mm deleted file mode 100644 index 18e2e2b4..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/AppDelegate.mm +++ /dev/null @@ -1,37 +0,0 @@ -#import "AppDelegate.h" - -#import -#import - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - self.moduleName = @"BitmovinPlayerReactNativeExample"; - // You can add your custom initial props in the dictionary below. - // They will be passed down to the ViewController used by React Native. - self.initialProps = @{}; - - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge -{ -#if DEBUG - return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; -#else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; -#endif -} - -/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. -/// -/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html -/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). -/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`. -- (BOOL)concurrentRootEnabled -{ - return true; -} - -@end diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/AccentColor.colorset/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb878970..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 2e003356..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json deleted file mode 100644 index de59d885..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "layers" : [ - { - "filename" : "Front.imagestacklayer" - }, - { - "filename" : "Middle.imagestacklayer" - }, - { - "filename" : "Back.imagestacklayer" - } - ] -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 2e003356..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 2e003356..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 795cce17..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv", - "scale" : "1x" - }, - { - "idiom" : "tv", - "scale" : "2x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json deleted file mode 100644 index de59d885..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - }, - "layers" : [ - { - "filename" : "Front.imagestacklayer" - }, - { - "filename" : "Middle.imagestacklayer" - }, - { - "filename" : "Back.imagestacklayer" - } - ] -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 795cce17..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv", - "scale" : "1x" - }, - { - "idiom" : "tv", - "scale" : "2x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json deleted file mode 100644 index 795cce17..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv", - "scale" : "1x" - }, - { - "idiom" : "tv", - "scale" : "2x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json deleted file mode 100644 index f47ba43d..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "assets" : [ - { - "filename" : "App Icon - App Store.imagestack", - "idiom" : "tv", - "role" : "primary-app-icon", - "size" : "1280x768" - }, - { - "filename" : "App Icon.imagestack", - "idiom" : "tv", - "role" : "primary-app-icon", - "size" : "400x240" - }, - { - "filename" : "Top Shelf Image Wide.imageset", - "idiom" : "tv", - "role" : "top-shelf-image-wide", - "size" : "2320x720" - }, - { - "filename" : "Top Shelf Image.imageset", - "idiom" : "tv", - "role" : "top-shelf-image", - "size" : "1920x720" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json deleted file mode 100644 index b65f0cdd..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv", - "scale" : "1x" - }, - { - "idiom" : "tv", - "scale" : "2x" - }, - { - "idiom" : "tv-marketing", - "scale" : "1x" - }, - { - "idiom" : "tv-marketing", - "scale" : "2x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json deleted file mode 100644 index b65f0cdd..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "images" : [ - { - "idiom" : "tv", - "scale" : "1x" - }, - { - "idiom" : "tv", - "scale" : "2x" - }, - { - "idiom" : "tv-marketing", - "scale" : "1x" - }, - { - "idiom" : "tv-marketing", - "scale" : "2x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/Contents.json b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/Contents.json deleted file mode 100644 index 73c00596..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Base.lproj/LaunchScreen.storyboard b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 660ba53d..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Debug.xcconfig b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Debug.xcconfig deleted file mode 100644 index e65f91b3..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-BitmovinPlayerReactNativeExample-tvOS/Pods-BitmovinPlayerReactNativeExample-tvOS.debug.xcconfig" -#include? "Developer.xcconfig" diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Info.plist b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Info.plist deleted file mode 100644 index 3ff3d801..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Info.plist +++ /dev/null @@ -1,12 +0,0 @@ - - - - - BitmovinPlayerLicenseKey - ENTER_LICENSE_KEY - UIBackgroundModes - - audio - - - diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Release.xcconfig b/example/ios/BitmovinPlayerReactNativeExample-tvOS/Release.xcconfig deleted file mode 100644 index e1687e69..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-BitmovinPlayerReactNativeExample-tvOS/Pods-BitmovinPlayerReactNativeExample-tvOS.release.xcconfig" -#include? "Developer.xcconfig" diff --git a/example/ios/BitmovinPlayerReactNativeExample-tvOS/main.m b/example/ios/BitmovinPlayerReactNativeExample-tvOS/main.m deleted file mode 100644 index a16d22cc..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample-tvOS/main.m +++ /dev/null @@ -1,8 +0,0 @@ -#import -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample.xcodeproj/project.pbxproj b/example/ios/BitmovinPlayerReactNativeExample.xcodeproj/project.pbxproj deleted file mode 100644 index 505f9516..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample.xcodeproj/project.pbxproj +++ /dev/null @@ -1,835 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 4CD973822836B701003409B0 /* AppDelegate.h in Sources */ = {isa = PBXBuildFile; fileRef = 4CD973812836B701003409B0 /* AppDelegate.h */; }; - 4CE382FA28BFD10E002DDF82 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4CE382F928BFD10E002DDF82 /* Assets.xcassets */; }; - 4CE382FD28BFD10E002DDF82 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4CE382FB28BFD10E002DDF82 /* LaunchScreen.storyboard */; }; - 4CE382FF28BFD10E002DDF82 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CE382FE28BFD10E002DDF82 /* main.m */; }; - 4CE3830528BFD270002DDF82 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4CE3830428BFD270002DDF82 /* AppDelegate.mm */; }; - 5915995436EFADAE00A71930 /* libPods-BitmovinPlayerReactNativeExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 06D60B6FD8A3297CA32930A9 /* libPods-BitmovinPlayerReactNativeExample.a */; }; - 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; - A8DB69432B31B5DB00BAD718 /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = A8DB69412B31B5DB00BAD718 /* Debug.xcconfig */; }; - A8DB69442B31B5DB00BAD718 /* Release.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = A8DB69422B31B5DB00BAD718 /* Release.xcconfig */; }; - A8DB69472B31B61400BAD718 /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = A8DB69452B31B61400BAD718 /* Debug.xcconfig */; }; - A8DB69482B31B61400BAD718 /* Release.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = A8DB69462B31B61400BAD718 /* Release.xcconfig */; }; - B3861322686908F1A14AEDFF /* libPods-BitmovinPlayerReactNativeExample-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B0574780FC6EB7D0D35EDF5 /* libPods-BitmovinPlayerReactNativeExample-tvOS.a */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 06D60B6FD8A3297CA32930A9 /* libPods-BitmovinPlayerReactNativeExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-BitmovinPlayerReactNativeExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07F961A680F5B00A75B9A /* BitmovinPlayerReactNativeExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BitmovinPlayerReactNativeExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = BitmovinPlayerReactNativeExample/AppDelegate.mm; sourceTree = ""; }; - 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = BitmovinPlayerReactNativeExample/Images.xcassets; sourceTree = ""; }; - 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = BitmovinPlayerReactNativeExample/Info.plist; sourceTree = ""; }; - 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = BitmovinPlayerReactNativeExample/main.m; sourceTree = ""; }; - 4451A76D9ED45CF044433360 /* Pods-BitmovinPlayerReactNativeExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BitmovinPlayerReactNativeExample.release.xcconfig"; path = "Target Support Files/Pods-BitmovinPlayerReactNativeExample/Pods-BitmovinPlayerReactNativeExample.release.xcconfig"; sourceTree = ""; }; - 4CA624FB28BFE2A2008D4FDA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; - 4CD973812836B701003409B0 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = BitmovinPlayerReactNativeExample/AppDelegate.h; sourceTree = ""; }; - 4CE382EE28BFD10C002DDF82 /* RN Player Example TV.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "RN Player Example TV.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 4CE382F928BFD10E002DDF82 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 4CE382FC28BFD10E002DDF82 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 4CE382FE28BFD10E002DDF82 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 4CE3830328BFD1E2002DDF82 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 4CE3830428BFD270002DDF82 /* AppDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AppDelegate.mm; sourceTree = ""; }; - 7B0574780FC6EB7D0D35EDF5 /* libPods-BitmovinPlayerReactNativeExample-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-BitmovinPlayerReactNativeExample-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 7D2D062A80C63508C52FD003 /* Pods-BitmovinPlayerReactNativeExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BitmovinPlayerReactNativeExample.debug.xcconfig"; path = "Target Support Files/Pods-BitmovinPlayerReactNativeExample/Pods-BitmovinPlayerReactNativeExample.debug.xcconfig"; sourceTree = ""; }; - 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = BitmovinPlayerReactNativeExample/LaunchScreen.storyboard; sourceTree = ""; }; - A8DB69412B31B5DB00BAD718 /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = BitmovinPlayerReactNativeExample/Debug.xcconfig; sourceTree = ""; }; - A8DB69422B31B5DB00BAD718 /* Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = BitmovinPlayerReactNativeExample/Release.xcconfig; sourceTree = ""; }; - A8DB69452B31B61400BAD718 /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - A8DB69462B31B61400BAD718 /* Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - BDAFF5A7A0416D4C35F3B620 /* Pods-BitmovinPlayerReactNativeExample-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BitmovinPlayerReactNativeExample-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-BitmovinPlayerReactNativeExample-tvOS/Pods-BitmovinPlayerReactNativeExample-tvOS.debug.xcconfig"; sourceTree = ""; }; - CAA1356D0B7800248D07E196 /* Pods-BitmovinPlayerReactNativeExample-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BitmovinPlayerReactNativeExample-tvOS.release.xcconfig"; path = "Target Support Files/Pods-BitmovinPlayerReactNativeExample-tvOS/Pods-BitmovinPlayerReactNativeExample-tvOS.release.xcconfig"; sourceTree = ""; }; - ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; - ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 5915995436EFADAE00A71930 /* libPods-BitmovinPlayerReactNativeExample.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CE382EB28BFD10C002DDF82 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - B3861322686908F1A14AEDFF /* libPods-BitmovinPlayerReactNativeExample-tvOS.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 13B07FAE1A68108700A75B9A /* BitmovinPlayerReactNativeExample */ = { - isa = PBXGroup; - children = ( - 4CD973812836B701003409B0 /* AppDelegate.h */, - 13B07FB01A68108700A75B9A /* AppDelegate.mm */, - 13B07FB51A68108700A75B9A /* Images.xcassets */, - 13B07FB61A68108700A75B9A /* Info.plist */, - 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, - 13B07FB71A68108700A75B9A /* main.m */, - A8DB69412B31B5DB00BAD718 /* Debug.xcconfig */, - A8DB69422B31B5DB00BAD718 /* Release.xcconfig */, - ); - name = BitmovinPlayerReactNativeExample; - sourceTree = ""; - }; - 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { - isa = PBXGroup; - children = ( - ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - ED2971642150620600B7C4FE /* JavaScriptCore.framework */, - 06D60B6FD8A3297CA32930A9 /* libPods-BitmovinPlayerReactNativeExample.a */, - 7B0574780FC6EB7D0D35EDF5 /* libPods-BitmovinPlayerReactNativeExample-tvOS.a */, - ); - name = Frameworks; - sourceTree = ""; - }; - 4CE382EF28BFD10C002DDF82 /* BitmovinPlayerReactNativeExample-tvOS */ = { - isa = PBXGroup; - children = ( - 4CA624FB28BFE2A2008D4FDA /* Info.plist */, - 4CE382F928BFD10E002DDF82 /* Assets.xcassets */, - 4CE382FB28BFD10E002DDF82 /* LaunchScreen.storyboard */, - 4CE382FE28BFD10E002DDF82 /* main.m */, - 4CE3830328BFD1E2002DDF82 /* AppDelegate.h */, - 4CE3830428BFD270002DDF82 /* AppDelegate.mm */, - A8DB69452B31B61400BAD718 /* Debug.xcconfig */, - A8DB69462B31B61400BAD718 /* Release.xcconfig */, - ); - path = "BitmovinPlayerReactNativeExample-tvOS"; - sourceTree = ""; - }; - 6B9684456A2045ADE5A6E47E /* Pods */ = { - isa = PBXGroup; - children = ( - 7D2D062A80C63508C52FD003 /* Pods-BitmovinPlayerReactNativeExample.debug.xcconfig */, - 4451A76D9ED45CF044433360 /* Pods-BitmovinPlayerReactNativeExample.release.xcconfig */, - BDAFF5A7A0416D4C35F3B620 /* Pods-BitmovinPlayerReactNativeExample-tvOS.debug.xcconfig */, - CAA1356D0B7800248D07E196 /* Pods-BitmovinPlayerReactNativeExample-tvOS.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; - 83CBB9F61A601CBA00E9B192 = { - isa = PBXGroup; - children = ( - 13B07FAE1A68108700A75B9A /* BitmovinPlayerReactNativeExample */, - 4CE382EF28BFD10C002DDF82 /* BitmovinPlayerReactNativeExample-tvOS */, - 83CBBA001A601CBA00E9B192 /* Products */, - 2D16E6871FA4F8E400B85C8A /* Frameworks */, - 6B9684456A2045ADE5A6E47E /* Pods */, - ); - indentWidth = 2; - sourceTree = ""; - tabWidth = 2; - usesTabs = 0; - }; - 83CBBA001A601CBA00E9B192 /* Products */ = { - isa = PBXGroup; - children = ( - 13B07F961A680F5B00A75B9A /* BitmovinPlayerReactNativeExample.app */, - 4CE382EE28BFD10C002DDF82 /* RN Player Example TV.app */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 13B07F861A680F5B00A75B9A /* BitmovinPlayerReactNativeExample */ = { - isa = PBXNativeTarget; - buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "BitmovinPlayerReactNativeExample" */; - buildPhases = ( - 5E3559E12AFFB82ED8822C46 /* [CP] Check Pods Manifest.lock */, - A8979BB02ADD2D9500821960 /* SwiftLint */, - 13B07F871A680F5B00A75B9A /* Sources */, - 13B07F8C1A680F5B00A75B9A /* Frameworks */, - 13B07F8E1A680F5B00A75B9A /* Resources */, - A2CB793523186225AE46687F /* [CP] Embed Pods Frameworks */, - 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - A46BFF240E7BC6134A18CB69 /* [CP] Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = BitmovinPlayerReactNativeExample; - productName = PlayerReactNativeBridgeExample; - productReference = 13B07F961A680F5B00A75B9A /* BitmovinPlayerReactNativeExample.app */; - productType = "com.apple.product-type.application"; - }; - 4CE382ED28BFD10C002DDF82 /* BitmovinPlayerReactNativeExample-tvOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 4CE3830228BFD10E002DDF82 /* Build configuration list for PBXNativeTarget "BitmovinPlayerReactNativeExample-tvOS" */; - buildPhases = ( - 3D81CB9D0E05C6D400C2E01A /* [CP] Check Pods Manifest.lock */, - A8979BB12ADD2DB500821960 /* SwiftLint */, - 4CE382EA28BFD10C002DDF82 /* Sources */, - 4CE382EB28BFD10C002DDF82 /* Frameworks */, - 4CE382EC28BFD10C002DDF82 /* Resources */, - 2C9AF44BCA3409CD3FDD5056 /* [CP] Embed Pods Frameworks */, - 4CA624F828BFDACB008D4FDA /* Bundle React Native code and images */, - 8BED34B25FCFA1CCB65C57DC /* [CP] Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "BitmovinPlayerReactNativeExample-tvOS"; - productName = "BitmovinPlayerReactNativeExample-tvOS"; - productReference = 4CE382EE28BFD10C002DDF82 /* RN Player Example TV.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 83CBB9F71A601CBA00E9B192 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1500; - TargetAttributes = { - 13B07F861A680F5B00A75B9A = { - LastSwiftMigration = 1330; - ProvisioningStyle = Automatic; - }; - 4CE382ED28BFD10C002DDF82 = { - CreatedOnToolsVersion = 13.4.1; - LastSwiftMigration = 1340; - ProvisioningStyle = Automatic; - }; - }; - }; - buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "BitmovinPlayerReactNativeExample" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 83CBB9F61A601CBA00E9B192; - productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 13B07F861A680F5B00A75B9A /* BitmovinPlayerReactNativeExample */, - 4CE382ED28BFD10C002DDF82 /* BitmovinPlayerReactNativeExample-tvOS */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 13B07F8E1A680F5B00A75B9A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A8DB69442B31B5DB00BAD718 /* Release.xcconfig in Resources */, - 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, - A8DB69432B31B5DB00BAD718 /* Debug.xcconfig in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CE382EC28BFD10C002DDF82 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A8DB69482B31B61400BAD718 /* Release.xcconfig in Resources */, - 4CE382FD28BFD10E002DDF82 /* LaunchScreen.storyboard in Resources */, - 4CE382FA28BFD10E002DDF82 /* Assets.xcassets in Resources */, - A8DB69472B31B61400BAD718 /* Debug.xcconfig in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "$(SRCROOT)/.xcode.env.local", - "$(SRCROOT)/.xcode.env", - ); - name = "Bundle React Native code and images"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "set -e\n\nexport SKIP_BUNDLING=true\n\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; - showEnvVarsInLog = 0; - }; - 2C9AF44BCA3409CD3FDD5056 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-BitmovinPlayerReactNativeExample-tvOS/Pods-BitmovinPlayerReactNativeExample-tvOS-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/BitmovinAnalyticsCollector/BitmovinPlayer/BitmovinCollector.framework/BitmovinCollector", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/BitmovinAnalyticsCollector/Core/CoreCollector.framework/CoreCollector", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/BitmovinPlayer/BitmovinPlayer.framework/BitmovinPlayer", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/BitmovinPlayer/BitmovinPlayerAnalytics.framework/BitmovinPlayerAnalytics", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/BitmovinPlayerCore/BitmovinPlayerCore.framework/BitmovinPlayerCore", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAds-IMA-tvOS-SDK/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BitmovinCollector.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CoreCollector.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BitmovinPlayer.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BitmovinPlayerAnalytics.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BitmovinPlayerCore.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleInteractiveMediaAds.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-BitmovinPlayerReactNativeExample-tvOS/Pods-BitmovinPlayerReactNativeExample-tvOS-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 3D81CB9D0E05C6D400C2E01A /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-BitmovinPlayerReactNativeExample-tvOS-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 4CA624F828BFDACB008D4FDA /* Bundle React Native code and images */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "$(SRCROOT)/.xcode.env", - "$(SRCROOT)/.xcode.env.local", - ); - name = "Bundle React Native code and images"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "set -e\n\nexport SKIP_BUNDLING=true\n\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; - }; - 5E3559E12AFFB82ED8822C46 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-BitmovinPlayerReactNativeExample-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 8BED34B25FCFA1CCB65C57DC /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-BitmovinPlayerReactNativeExample-tvOS/Pods-BitmovinPlayerReactNativeExample-tvOS-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Core-tvOS/RCTI18nStrings.bundle", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-BitmovinPlayerReactNativeExample-tvOS/Pods-BitmovinPlayerReactNativeExample-tvOS-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - A2CB793523186225AE46687F /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-BitmovinPlayerReactNativeExample/Pods-BitmovinPlayerReactNativeExample-frameworks.sh", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/BitmovinAnalyticsCollector/BitmovinPlayer/BitmovinCollector.framework/BitmovinCollector", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/BitmovinAnalyticsCollector/Core/CoreCollector.framework/CoreCollector", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/BitmovinPlayer/BitmovinPlayer.framework/BitmovinPlayer", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/BitmovinPlayer/BitmovinPlayerAnalytics.framework/BitmovinPlayerAnalytics", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/BitmovinPlayerCore/BitmovinPlayerCore.framework/BitmovinPlayerCore", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAds-IMA-iOS-SDK/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BitmovinCollector.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CoreCollector.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BitmovinPlayer.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BitmovinPlayerAnalytics.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BitmovinPlayerCore.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleInteractiveMediaAds.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-BitmovinPlayerReactNativeExample/Pods-BitmovinPlayerReactNativeExample-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - A46BFF240E7BC6134A18CB69 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-BitmovinPlayerReactNativeExample/Pods-BitmovinPlayerReactNativeExample-resources.sh", - "${PODS_CONFIGURATION_BUILD_DIR}/Protobuf/Protobuf_Privacy.bundle", - "${PODS_CONFIGURATION_BUILD_DIR}/React-Core-iOS/RCTI18nStrings.bundle", - "${PODS_ROOT}/google-cast-sdk/Resources/GoogleCastCoreResources.bundle", - "${PODS_ROOT}/google-cast-sdk/Resources/GoogleCastUIResources.bundle", - "${PODS_ROOT}/google-cast-sdk/Resources/MaterialDialogs.bundle", - "${PODS_ROOT}/google-cast-sdk/GoogleCast.xcframework/ios-arm64/GoogleCast.framework/PrivacyInfo.xcprivacy", - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Protobuf_Privacy.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleCastCoreResources.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleCastUIResources.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialDialogs.bundle", - "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/PrivacyInfo.xcprivacy", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-BitmovinPlayerReactNativeExample/Pods-BitmovinPlayerReactNativeExample-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - A8979BB02ADD2D9500821960 /* SwiftLint */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = SwiftLint; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\nfi\n\n(\n cd ../../\n if which swiftlint > /dev/null; then\n swiftlint\n else\n echo \"warning: SwiftLint not installed, run \\`brew bundle install\\` in project root to install\"\n fi\n)\n"; - showEnvVarsInLog = 0; - }; - A8979BB12ADD2DB500821960 /* SwiftLint */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = SwiftLint; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\nfi\n\n(\n cd ../../\n if which swiftlint > /dev/null; then\n swiftlint\n else\n echo \"warning: SwiftLint not installed, run \\`brew bundle install\\` in project root to install\"\n fi\n)\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 13B07F871A680F5B00A75B9A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4CD973822836B701003409B0 /* AppDelegate.h in Sources */, - 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, - 13B07FC11A68108700A75B9A /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 4CE382EA28BFD10C002DDF82 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 4CE3830528BFD270002DDF82 /* AppDelegate.mm in Sources */, - 4CE382FF28BFD10E002DDF82 /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 4CE382FB28BFD10E002DDF82 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 4CE382FC28BFD10E002DDF82 /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 13B07F941A680F5B00A75B9A /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A8DB69412B31B5DB00BAD718 /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_CXX_LANGUAGE_STANDARD = "c++20"; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = BitmovinPlayerReactNativeExample/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.bitmovin.PlayerReactNative-Example"; - PRODUCT_NAME = BitmovinPlayerReactNativeExample; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OBJC_BRIDGING_HEADER = "BitmovinPlayerReactNativeExample-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 13B07F951A680F5B00A75B9A /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A8DB69422B31B5DB00BAD718 /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_CXX_LANGUAGE_STANDARD = "c++20"; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - INFOPLIST_FILE = BitmovinPlayerReactNativeExample/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "com.bitmovin.PlayerReactNative-Example"; - PRODUCT_NAME = BitmovinPlayerReactNativeExample; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OBJC_BRIDGING_HEADER = "BitmovinPlayerReactNativeExample-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; - 4CE3830028BFD10E002DDF82 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A8DB69452B31B61400BAD718 /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GCC_C_LANGUAGE_STANDARD = gnu11; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "BitmovinPlayerReactNativeExample-tvOS/Info.plist"; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "com.bitmovin.PlayerReactNativeExample-tvOS"; - PRODUCT_NAME = "RN Player Example TV"; - SDKROOT = appletvos; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 14.0; - }; - name = Debug; - }; - 4CE3830128BFD10E002DDF82 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = A8DB69462B31B61400BAD718 /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - GCC_C_LANGUAGE_STANDARD = gnu11; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "BitmovinPlayerReactNativeExample-tvOS/Info.plist"; - INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; - INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "com.bitmovin.PlayerReactNativeExample-tvOS"; - PRODUCT_NAME = "RN Player Example TV"; - SDKROOT = appletvos; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 14.0; - }; - name = Release; - }; - 83CBBA201A601CBA00E9B192 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_CXX_LANGUAGE_STANDARD = "c++20"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - "EXCLUDED_ARCHS[sdk=appletvsimulator*]" = i386; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - _LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION, - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = ( - /usr/lib/swift, - "$(inherited)", - ); - LIBRARY_SEARCH_PATHS = ( - "$(SDKROOT)/usr/lib/swift", - "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", - "\"$(inherited)\"", - ); - MARKETING_VERSION = 1.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - OTHER_CFLAGS = "$(inherited)"; - OTHER_CPLUSPLUSFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; - SDKROOT = iphoneos; - TVOS_DEPLOYMENT_TARGET = 14.0; - USE_HERMES = true; - }; - name = Debug; - }; - 83CBBA211A601CBA00E9B192 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_CXX_LANGUAGE_STANDARD = "c++20"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - "EXCLUDED_ARCHS[sdk=appletvsimulator*]" = i386; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "$(inherited)", - _LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION, - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = ( - /usr/lib/swift, - "$(inherited)", - ); - LIBRARY_SEARCH_PATHS = ( - "$(SDKROOT)/usr/lib/swift", - "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", - "\"$(inherited)\"", - ); - MARKETING_VERSION = 1.0; - MTL_ENABLE_DEBUG_INFO = NO; - OTHER_CFLAGS = "$(inherited)"; - OTHER_CPLUSPLUSFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; - SDKROOT = iphoneos; - TVOS_DEPLOYMENT_TARGET = 14.0; - USE_HERMES = true; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "BitmovinPlayerReactNativeExample" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 13B07F941A680F5B00A75B9A /* Debug */, - 13B07F951A680F5B00A75B9A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 4CE3830228BFD10E002DDF82 /* Build configuration list for PBXNativeTarget "BitmovinPlayerReactNativeExample-tvOS" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 4CE3830028BFD10E002DDF82 /* Debug */, - 4CE3830128BFD10E002DDF82 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "BitmovinPlayerReactNativeExample" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 83CBBA201A601CBA00E9B192 /* Debug */, - 83CBBA211A601CBA00E9B192 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; -} diff --git a/example/ios/BitmovinPlayerReactNativeExample.xcodeproj/xcshareddata/xcschemes/BitmovinPlayerReactNativeExample-tvOS.xcscheme b/example/ios/BitmovinPlayerReactNativeExample.xcodeproj/xcshareddata/xcschemes/BitmovinPlayerReactNativeExample-tvOS.xcscheme deleted file mode 100644 index dc498113..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample.xcodeproj/xcshareddata/xcschemes/BitmovinPlayerReactNativeExample-tvOS.xcscheme +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/example/ios/BitmovinPlayerReactNativeExample.xcodeproj/xcshareddata/xcschemes/BitmovinPlayerReactNativeExample.xcscheme b/example/ios/BitmovinPlayerReactNativeExample.xcodeproj/xcshareddata/xcschemes/BitmovinPlayerReactNativeExample.xcscheme deleted file mode 100644 index 771ac4f4..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample.xcodeproj/xcshareddata/xcschemes/BitmovinPlayerReactNativeExample.xcscheme +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/example/ios/BitmovinPlayerReactNativeExample.xcworkspace/contents.xcworkspacedata b/example/ios/BitmovinPlayerReactNativeExample.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 6c98ce9f..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/example/ios/BitmovinPlayerReactNativeExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/BitmovinPlayerReactNativeExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/example/ios/BitmovinPlayerReactNativeExample/AppDelegate.h b/example/ios/BitmovinPlayerReactNativeExample/AppDelegate.h deleted file mode 100644 index ac490f9b..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample/AppDelegate.h +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import -#import - -@interface AppDelegate : RCTAppDelegate - -@end diff --git a/example/ios/BitmovinPlayerReactNativeExample/AppDelegate.mm b/example/ios/BitmovinPlayerReactNativeExample/AppDelegate.mm deleted file mode 100644 index 236dacb4..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample/AppDelegate.mm +++ /dev/null @@ -1,50 +0,0 @@ -#import "Orientation.h" -#import "AppDelegate.h" - -#import -#import - -@implementation AppDelegate - -- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window { - return [Orientation getOrientation]; -} - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - self.moduleName = @"BitmovinPlayerReactNativeExample"; - // You can add your custom initial props in the dictionary below. - // They will be passed down to the ViewController used by React Native. - self.initialProps = @{}; - - // Only needed if the offline feature is used - [BMPOfflineManager initializeOfflineManager]; - - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge -{ -#if DEBUG - return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; -#else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; -#endif -} - -// Only needed if the offline feature is used -- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler { - [[BMPOfflineManager sharedInstance] addCompletionHandler:completionHandler forIdentifier:identifier]; -} - -/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. -/// -/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html -/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). -/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`. -- (BOOL)concurrentRootEnabled -{ - return true; -} - -@end diff --git a/example/ios/BitmovinPlayerReactNativeExample/Debug.xcconfig b/example/ios/BitmovinPlayerReactNativeExample/Debug.xcconfig deleted file mode 100644 index 1438a4d6..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-BitmovinPlayerReactNativeExample/Pods-BitmovinPlayerReactNativeExample.debug.xcconfig" -#include? "Developer.xcconfig" diff --git a/example/ios/BitmovinPlayerReactNativeExample/Images.xcassets/AppIcon.appiconset/Contents.json b/example/ios/BitmovinPlayerReactNativeExample/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 81213230..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "60x60" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "60x60" - }, - { - "idiom" : "ios-marketing", - "scale" : "1x", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample/Images.xcassets/Contents.json b/example/ios/BitmovinPlayerReactNativeExample/Images.xcassets/Contents.json deleted file mode 100644 index 2d92bd53..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample/Images.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/example/ios/BitmovinPlayerReactNativeExample/Info.plist b/example/ios/BitmovinPlayerReactNativeExample/Info.plist deleted file mode 100644 index 2875f460..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample/Info.plist +++ /dev/null @@ -1,71 +0,0 @@ - - - - - BitmovinPlayerLicenseKey - 60bd09ff-5a03-45ed-9453-e832798d1c7e - CFBundleDevelopmentRegion - en - CFBundleDisplayName - RN Player Example - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - LSRequiresIPhoneOS - - NSAppTransportSecurity - - - NSAllowsArbitraryLoads - - NSAllowsLocalNetworking - - - NSLocationWhenInUseUsageDescription - - UIBackgroundModes - - audio - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - NSBonjourServices - - _googlecast._tcp - _FFE417E5._googlecast._tcp - - NSLocalNetworkUsageDescription - ${PRODUCT_NAME} uses the local network to discover Cast-enabled devices on your WiFi network. - NSMicrophoneUsageDescription - Chromecast requires access to microphone - NSBluetoothAlwaysUsageDescription - Chromecast requires this - NSBluetoothPeripheralUsageDescription - Chromecast requires access to Bluetooth - - diff --git a/example/ios/BitmovinPlayerReactNativeExample/LaunchScreen.storyboard b/example/ios/BitmovinPlayerReactNativeExample/LaunchScreen.storyboard deleted file mode 100644 index 41bda963..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample/LaunchScreen.storyboard +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/example/ios/BitmovinPlayerReactNativeExample/Release.xcconfig b/example/ios/BitmovinPlayerReactNativeExample/Release.xcconfig deleted file mode 100644 index f32ea81c..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-BitmovinPlayerReactNativeExample/Pods-BitmovinPlayerReactNativeExample.release.xcconfig" -#include? "Developer.xcconfig" diff --git a/example/ios/BitmovinPlayerReactNativeExample/main.m b/example/ios/BitmovinPlayerReactNativeExample/main.m deleted file mode 100644 index c316cf81..00000000 --- a/example/ios/BitmovinPlayerReactNativeExample/main.m +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import - -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/example/ios/Podfile b/example/ios/Podfile deleted file mode 100644 index 1f1edce6..00000000 --- a/example/ios/Podfile +++ /dev/null @@ -1,115 +0,0 @@ -require_relative '../node_modules/react-native/scripts/react_native_pods' -require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' - -source 'https://cdn.cocoapods.org' -source 'https://github.com/react-native-tvos/react-native-tvos-podspecs.git' - -prepare_react_native_project! - -def setup os - inhibit_all_warnings! - platform os, '14.0' - - # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. - # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded - # - # To fix this you can also exclude `react-native-flipper` using a `react-native.config.js` - # ```js - # module.exports = { - # dependencies: { - # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}), - # ``` - flipper_config = (ENV['NO_FLIPPER'] == "1" || os != :ios) ? FlipperConfiguration.disabled : FlipperConfiguration.enabled - linkage = ENV['USE_FRAMEWORKS'] - if linkage != nil - Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green - use_frameworks! :linkage => linkage.to_sym - end - - config = use_native_modules! - - # Flags change depending on the env values. - flags = get_default_flags() - - use_react_native!( - # Enables Flipper. - # - # Note that if you have use_frameworks! enabled, Flipper will not work and - # you should disable the next line. - :flipper_configuration => flipper_config, - :path => config[:reactNativePath], - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/.." - ) - - pod 'RNBitmovinPlayer', :path => '../..' -end - -target 'BitmovinPlayerReactNativeExample' do - setup :ios - pod 'google-cast-sdk', '4.8.1' -end - -target 'BitmovinPlayerReactNativeExample-tvOS' do - setup :tvos -end - -post_install do |installer| - react_native_post_install( - installer, - # Set `mac_catalyst_enabled` to `true` in order to apply patches - # necessary for Mac Catalyst builds - :mac_catalyst_enabled => false - ) - fix_simulator_run(installer) - fix_deployment_target(installer) - disable_resource_bundle_signing(installer) -end - -# Workaround for running on simulator on Apple Silicon from the command-line -def fix_simulator_run(installer) - installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" - config.build_settings["EXCLUDED_ARCHS[sdk=appletvsimulator*]"] = "i386" - end - end -end - -# Align deployment target of all targets in the Pods project with the main project -def fix_deployment_target(installer) - return if !installer - project = installer.pods_project - project_deployment_target_iOS = project.build_configurations.first.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] - project_deployment_target_tvOS = project.build_configurations.first.build_settings['TVOS_DEPLOYMENT_TARGET'] - - project.targets.each do |target| - target.build_configurations.each do |config| - old_target_iOS = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] - unless old_target_iOS.nil? - new_target_iOS = project_deployment_target_iOS - next if old_target_iOS.to_f >= new_target_iOS.to_f - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = new_target_iOS - end - - old_target_tvOS = config.build_settings['TVOS_DEPLOYMENT_TARGET'] - unless old_target_tvOS.nil? - new_target_tvOS = project_deployment_target_tvOS - next if old_target_tvOS.to_f >= new_target_tvOS.to_f - config.build_settings['TVOS_DEPLOYMENT_TARGET'] = new_target_tvOS - end - end - end -end - -# Workaround against required code-signing for resource bundle targets -def disable_resource_bundle_signing(installer) - installer.pods_project.targets.each do |target| - target_is_resource_bundle = target.respond_to?(:product_type) && target.product_type == 'com.apple.product-type.bundle' - target.build_configurations.each do |config| - if target_is_resource_bundle - config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' - end - end - end -end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock deleted file mode 100644 index eba0a327..00000000 --- a/example/ios/Podfile.lock +++ /dev/null @@ -1,1312 +0,0 @@ -PODS: - - BitmovinAnalyticsCollector/BitmovinPlayer (3.9.0): - - BitmovinAnalyticsCollector/Core - - BitmovinPlayerCore (~> 3.48) - - BitmovinAnalyticsCollector/Core (3.9.0) - - BitmovinPlayer (3.81.0): - - BitmovinAnalyticsCollector/BitmovinPlayer (~> 3.0) - - BitmovinPlayerCore (= 3.81.0) - - BitmovinPlayerCore (3.81.0) - - boost (1.83.0) - - DoubleConversion (1.1.6) - - FBLazyVector (0.73.4-0) - - FBReactNativeSpec (0.73.4-0): - - RCT-Folly (= 2022.05.16.00) - - RCTRequired (= 0.73.4-0) - - RCTTypeSafety (= 0.73.4-0) - - React-Core (= 0.73.4-0) - - React-jsi (= 0.73.4-0) - - ReactCommon/turbomodule/core (= 0.73.4-0) - - fmt (6.2.1) - - glog (0.3.5) - - google-cast-sdk (4.8.1): - - Protobuf (~> 3.13) - - GoogleAds-IMA-iOS-SDK (3.23.0) - - GoogleAds-IMA-tvOS-SDK (4.13.0) - - hermes-engine (0.73.4-0): - - hermes-engine/Pre-built (= 0.73.4-0) - - hermes-engine/Pre-built (0.73.4-0) - - libevent (2.1.12.1) - - Protobuf (3.27.1) - - RCT-Folly (2022.05.16.00): - - boost - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - RCT-Folly/Default (= 2022.05.16.00) - - RCT-Folly/Default (2022.05.16.00): - - boost - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - RCT-Folly/Fabric (2022.05.16.00): - - boost - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - RCT-Folly/Futures (2022.05.16.00): - - boost - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - libevent - - RCTRequired (0.73.4-0) - - RCTTypeSafety (0.73.4-0): - - FBLazyVector (= 0.73.4-0) - - RCTRequired (= 0.73.4-0) - - React-Core (= 0.73.4-0) - - React (0.73.4-0): - - React-Core (= 0.73.4-0) - - React-Core/DevSupport (= 0.73.4-0) - - React-Core/RCTWebSocket (= 0.73.4-0) - - React-RCTActionSheet (= 0.73.4-0) - - React-RCTAnimation (= 0.73.4-0) - - React-RCTBlob (= 0.73.4-0) - - React-RCTImage (= 0.73.4-0) - - React-RCTLinking (= 0.73.4-0) - - React-RCTNetwork (= 0.73.4-0) - - React-RCTSettings (= 0.73.4-0) - - React-RCTText (= 0.73.4-0) - - React-callinvoker (0.73.4-0) - - React-Codegen (0.73.4-0): - - DoubleConversion - - FBReactNativeSpec - - glog - - hermes-engine - - RCT-Folly - - RCTRequired - - RCTTypeSafety - - React-Core - - React-jsi - - React-jsiexecutor - - React-NativeModulesApple - - React-rncore - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - React-Core (0.73.4-0): - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-Core/Default (= 0.73.4-0) - - React-cxxreact - - React-hermes - - React-jsi - - React-jsiexecutor - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.6.1) - - Yoga - - React-Core/CoreModulesHeaders (0.73.4-0): - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-Core/Default - - React-cxxreact - - React-hermes - - React-jsi - - React-jsiexecutor - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.6.1) - - Yoga - - React-Core/Default (0.73.4-0): - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-cxxreact - - React-hermes - - React-jsi - - React-jsiexecutor - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.6.1) - - Yoga - - React-Core/DevSupport (0.73.4-0): - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-Core/Default (= 0.73.4-0) - - React-Core/RCTWebSocket (= 0.73.4-0) - - React-cxxreact - - React-hermes - - React-jsi - - React-jsiexecutor - - React-jsinspector (= 0.73.4-0) - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.6.1) - - Yoga - - React-Core/RCTActionSheetHeaders (0.73.4-0): - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-Core/Default - - React-cxxreact - - React-hermes - - React-jsi - - React-jsiexecutor - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.6.1) - - Yoga - - React-Core/RCTAnimationHeaders (0.73.4-0): - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-Core/Default - - React-cxxreact - - React-hermes - - React-jsi - - React-jsiexecutor - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.6.1) - - Yoga - - React-Core/RCTBlobHeaders (0.73.4-0): - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-Core/Default - - React-cxxreact - - React-hermes - - React-jsi - - React-jsiexecutor - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.6.1) - - Yoga - - React-Core/RCTImageHeaders (0.73.4-0): - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-Core/Default - - React-cxxreact - - React-hermes - - React-jsi - - React-jsiexecutor - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.6.1) - - Yoga - - React-Core/RCTLinkingHeaders (0.73.4-0): - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-Core/Default - - React-cxxreact - - React-hermes - - React-jsi - - React-jsiexecutor - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.6.1) - - Yoga - - React-Core/RCTNetworkHeaders (0.73.4-0): - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-Core/Default - - React-cxxreact - - React-hermes - - React-jsi - - React-jsiexecutor - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.6.1) - - Yoga - - React-Core/RCTSettingsHeaders (0.73.4-0): - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-Core/Default - - React-cxxreact - - React-hermes - - React-jsi - - React-jsiexecutor - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.6.1) - - Yoga - - React-Core/RCTTextHeaders (0.73.4-0): - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-Core/Default - - React-cxxreact - - React-hermes - - React-jsi - - React-jsiexecutor - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.6.1) - - Yoga - - React-Core/RCTWebSocket (0.73.4-0): - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-Core/Default (= 0.73.4-0) - - React-cxxreact - - React-hermes - - React-jsi - - React-jsiexecutor - - React-perflogger - - React-runtimescheduler - - React-utils - - SocketRocket (= 0.6.1) - - Yoga - - React-CoreModules (0.73.4-0): - - RCT-Folly (= 2022.05.16.00) - - RCTTypeSafety (= 0.73.4-0) - - React-Codegen - - React-Core/CoreModulesHeaders (= 0.73.4-0) - - React-jsi (= 0.73.4-0) - - React-NativeModulesApple - - React-RCTBlob - - React-RCTImage (= 0.73.4-0) - - ReactCommon - - SocketRocket (= 0.6.1) - - React-cxxreact (0.73.4-0): - - boost (= 1.83.0) - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-callinvoker (= 0.73.4-0) - - React-debug (= 0.73.4-0) - - React-jsi (= 0.73.4-0) - - React-jsinspector (= 0.73.4-0) - - React-logger (= 0.73.4-0) - - React-perflogger (= 0.73.4-0) - - React-runtimeexecutor (= 0.73.4-0) - - React-debug (0.73.4-0) - - React-Fabric (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric/animations (= 0.73.4-0) - - React-Fabric/attributedstring (= 0.73.4-0) - - React-Fabric/componentregistry (= 0.73.4-0) - - React-Fabric/componentregistrynative (= 0.73.4-0) - - React-Fabric/components (= 0.73.4-0) - - React-Fabric/core (= 0.73.4-0) - - React-Fabric/imagemanager (= 0.73.4-0) - - React-Fabric/leakchecker (= 0.73.4-0) - - React-Fabric/mounting (= 0.73.4-0) - - React-Fabric/scheduler (= 0.73.4-0) - - React-Fabric/telemetry (= 0.73.4-0) - - React-Fabric/templateprocessor (= 0.73.4-0) - - React-Fabric/textlayoutmanager (= 0.73.4-0) - - React-Fabric/uimanager (= 0.73.4-0) - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/animations (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/attributedstring (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/componentregistry (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/componentregistrynative (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric/components/inputaccessory (= 0.73.4-0) - - React-Fabric/components/legacyviewmanagerinterop (= 0.73.4-0) - - React-Fabric/components/modal (= 0.73.4-0) - - React-Fabric/components/rncore (= 0.73.4-0) - - React-Fabric/components/root (= 0.73.4-0) - - React-Fabric/components/safeareaview (= 0.73.4-0) - - React-Fabric/components/scrollview (= 0.73.4-0) - - React-Fabric/components/text (= 0.73.4-0) - - React-Fabric/components/textinput (= 0.73.4-0) - - React-Fabric/components/unimplementedview (= 0.73.4-0) - - React-Fabric/components/view (= 0.73.4-0) - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components/inputaccessory (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components/legacyviewmanagerinterop (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components/modal (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components/rncore (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components/root (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components/safeareaview (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components/scrollview (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components/text (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components/textinput (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components/unimplementedview (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/components/view (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - Yoga - - React-Fabric/core (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/imagemanager (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/leakchecker (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/mounting (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/scheduler (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/telemetry (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/templateprocessor (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/textlayoutmanager (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-Fabric/uimanager - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-Fabric/uimanager (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired - - RCTTypeSafety - - React-Core - - React-cxxreact - - React-debug - - React-graphics - - React-jsi - - React-jsiexecutor - - React-logger - - React-rendererdebug - - React-runtimescheduler - - React-utils - - ReactCommon/turbomodule/core - - React-FabricImage (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - RCTRequired (= 0.73.4-0) - - RCTTypeSafety (= 0.73.4-0) - - React-Fabric - - React-graphics - - React-ImageManager - - React-jsi - - React-jsiexecutor (= 0.73.4-0) - - React-logger - - React-rendererdebug - - React-utils - - ReactCommon - - Yoga - - React-graphics (0.73.4-0): - - glog - - RCT-Folly/Fabric (= 2022.05.16.00) - - React-Core/Default (= 0.73.4-0) - - React-utils - - React-hermes (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - RCT-Folly/Futures (= 2022.05.16.00) - - React-cxxreact (= 0.73.4-0) - - React-jsi - - React-jsiexecutor (= 0.73.4-0) - - React-jsinspector (= 0.73.4-0) - - React-perflogger (= 0.73.4-0) - - React-ImageManager (0.73.4-0): - - glog - - RCT-Folly/Fabric - - React-Core/Default - - React-debug - - React-Fabric - - React-graphics - - React-rendererdebug - - React-utils - - React-jserrorhandler (0.73.4-0): - - RCT-Folly/Fabric (= 2022.05.16.00) - - React-debug - - React-jsi - - React-Mapbuffer - - React-jsi (0.73.4-0): - - boost (= 1.83.0) - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-jsiexecutor (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-cxxreact (= 0.73.4-0) - - React-jsi (= 0.73.4-0) - - React-perflogger (= 0.73.4-0) - - React-jsinspector (0.73.4-0) - - React-logger (0.73.4-0): - - glog - - React-Mapbuffer (0.73.4-0): - - glog - - React-debug - - react-native-orientation-locker (1.6.0): - - React-Core - - react-native-safe-area-context (4.9.0): - - React-Core - - React-nativeconfig (0.73.4-0) - - React-NativeModulesApple (0.73.4-0): - - glog - - hermes-engine - - React-callinvoker - - React-Core - - React-cxxreact - - React-jsi - - React-runtimeexecutor - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - React-perflogger (0.73.4-0) - - React-RCTActionSheet (0.73.4-0): - - React-Core/RCTActionSheetHeaders (= 0.73.4-0) - - React-RCTAnimation (0.73.4-0): - - RCT-Folly (= 2022.05.16.00) - - RCTTypeSafety - - React-Codegen - - React-Core/RCTAnimationHeaders - - React-jsi - - React-NativeModulesApple - - ReactCommon - - React-RCTAppDelegate (0.73.4-0): - - RCT-Folly - - RCTRequired - - RCTTypeSafety - - React-Core - - React-CoreModules - - React-hermes - - React-nativeconfig - - React-NativeModulesApple - - React-RCTFabric - - React-RCTImage - - React-RCTNetwork - - React-runtimescheduler - - ReactCommon - - React-RCTBlob (0.73.4-0): - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-Codegen - - React-Core/RCTBlobHeaders - - React-Core/RCTWebSocket - - React-jsi - - React-NativeModulesApple - - React-RCTNetwork - - ReactCommon - - React-RCTFabric (0.73.4-0): - - glog - - hermes-engine - - RCT-Folly/Fabric (= 2022.05.16.00) - - React-Core - - React-debug - - React-Fabric - - React-FabricImage - - React-graphics - - React-ImageManager - - React-jsi - - React-nativeconfig - - React-RCTImage - - React-RCTText - - React-rendererdebug - - React-runtimescheduler - - React-utils - - Yoga - - React-RCTImage (0.73.4-0): - - RCT-Folly (= 2022.05.16.00) - - RCTTypeSafety - - React-Codegen - - React-Core/RCTImageHeaders - - React-jsi - - React-NativeModulesApple - - React-RCTNetwork - - ReactCommon - - React-RCTLinking (0.73.4-0): - - React-Codegen - - React-Core/RCTLinkingHeaders (= 0.73.4-0) - - React-jsi (= 0.73.4-0) - - React-NativeModulesApple - - ReactCommon - - ReactCommon/turbomodule/core (= 0.73.4-0) - - React-RCTNetwork (0.73.4-0): - - RCT-Folly (= 2022.05.16.00) - - RCTTypeSafety - - React-Codegen - - React-Core/RCTNetworkHeaders - - React-jsi - - React-NativeModulesApple - - ReactCommon - - React-RCTSettings (0.73.4-0): - - RCT-Folly (= 2022.05.16.00) - - RCTTypeSafety - - React-Codegen - - React-Core/RCTSettingsHeaders - - React-jsi - - React-NativeModulesApple - - ReactCommon - - React-RCTText (0.73.4-0): - - React-Core/RCTTextHeaders (= 0.73.4-0) - - Yoga - - React-rendererdebug (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - RCT-Folly (= 2022.05.16.00) - - React-debug - - React-rncore (0.73.4-0) - - React-runtimeexecutor (0.73.4-0): - - React-jsi (= 0.73.4-0) - - React-runtimescheduler (0.73.4-0): - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-callinvoker - - React-cxxreact - - React-debug - - React-jsi - - React-rendererdebug - - React-runtimeexecutor - - React-utils - - React-utils (0.73.4-0): - - glog - - RCT-Folly (= 2022.05.16.00) - - React-debug - - ReactCommon (0.73.4-0): - - React-logger (= 0.73.4-0) - - ReactCommon/turbomodule (= 0.73.4-0) - - ReactCommon/turbomodule (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-callinvoker (= 0.73.4-0) - - React-cxxreact (= 0.73.4-0) - - React-jsi (= 0.73.4-0) - - React-logger (= 0.73.4-0) - - React-perflogger (= 0.73.4-0) - - ReactCommon/turbomodule/bridging (= 0.73.4-0) - - ReactCommon/turbomodule/core (= 0.73.4-0) - - ReactCommon/turbomodule/bridging (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-callinvoker (= 0.73.4-0) - - React-cxxreact (= 0.73.4-0) - - React-jsi (= 0.73.4-0) - - React-logger (= 0.73.4-0) - - React-perflogger (= 0.73.4-0) - - ReactCommon/turbomodule/core (0.73.4-0): - - DoubleConversion - - fmt (~> 6.2.1) - - glog - - hermes-engine - - RCT-Folly (= 2022.05.16.00) - - React-callinvoker (= 0.73.4-0) - - React-cxxreact (= 0.73.4-0) - - React-jsi (= 0.73.4-0) - - React-logger (= 0.73.4-0) - - React-perflogger (= 0.73.4-0) - - RNBitmovinPlayer (0.37.0): - - BitmovinPlayer (= 3.81.0) - - GoogleAds-IMA-iOS-SDK (= 3.23.0) - - GoogleAds-IMA-tvOS-SDK (= 4.13.0) - - React-Core - - RNCPicker (2.6.1): - - React-Core - - RNScreens (3.29.0): - - glog - - RCT-Folly (= 2022.05.16.00) - - React-Core - - SocketRocket (0.6.1) - - Yoga (1.14.0) - -DEPENDENCIES: - - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) - - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`) - - fmt (from `../node_modules/react-native/third-party-podspecs/fmt.podspec`) - - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - - google-cast-sdk (= 4.8.1) - - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - - libevent (~> 2.1.12) - - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - - RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) - - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) - - React (from `../node_modules/react-native/`) - - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) - - React-Codegen (from `build/generated/ios`) - - React-Core (from `../node_modules/react-native/`) - - React-Core/RCTWebSocket (from `../node_modules/react-native/`) - - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) - - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) - - React-debug (from `../node_modules/react-native/ReactCommon/react/debug`) - - React-Fabric (from `../node_modules/react-native/ReactCommon`) - - React-FabricImage (from `../node_modules/react-native/ReactCommon`) - - React-graphics (from `../node_modules/react-native/ReactCommon/react/renderer/graphics`) - - React-hermes (from `../node_modules/react-native/ReactCommon/hermes`) - - React-ImageManager (from `../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios`) - - React-jserrorhandler (from `../node_modules/react-native/ReactCommon/jserrorhandler`) - - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) - - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) - - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector-modern`) - - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) - - react-native-orientation-locker (from `../node_modules/react-native-orientation-locker`) - - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - - React-nativeconfig (from `../node_modules/react-native/ReactCommon`) - - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) - - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) - - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) - - React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`) - - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) - - React-RCTFabric (from `../node_modules/react-native/React`) - - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) - - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) - - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) - - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) - - React-RCTText (from `../node_modules/react-native/Libraries/Text`) - - React-rendererdebug (from `../node_modules/react-native/ReactCommon/react/renderer/debug`) - - React-rncore (from `../node_modules/react-native/ReactCommon`) - - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) - - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) - - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) - - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - - RNBitmovinPlayer (from `../..`) - - "RNCPicker (from `../node_modules/@react-native-picker/picker`)" - - RNScreens (from `../node_modules/react-native-screens`) - - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) - -SPEC REPOS: - https://github.com/react-native-tvos/react-native-tvos-podspecs.git: - - libevent - trunk: - - BitmovinAnalyticsCollector - - BitmovinPlayer - - BitmovinPlayerCore - - google-cast-sdk - - GoogleAds-IMA-iOS-SDK - - GoogleAds-IMA-tvOS-SDK - - Protobuf - - SocketRocket - -EXTERNAL SOURCES: - boost: - :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec" - DoubleConversion: - :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" - FBLazyVector: - :path: "../node_modules/react-native/Libraries/FBLazyVector" - FBReactNativeSpec: - :path: "../node_modules/react-native/React/FBReactNativeSpec" - fmt: - :podspec: "../node_modules/react-native/third-party-podspecs/fmt.podspec" - glog: - :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" - hermes-engine: - :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" - :tag: hermes-2024-01-31-RNv0.73.3-398783c198253f61e0a5eb603f1eb7b55af6baa4 - RCT-Folly: - :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" - RCTRequired: - :path: "../node_modules/react-native/Libraries/RCTRequired" - RCTTypeSafety: - :path: "../node_modules/react-native/Libraries/TypeSafety" - React: - :path: "../node_modules/react-native/" - React-callinvoker: - :path: "../node_modules/react-native/ReactCommon/callinvoker" - React-Codegen: - :path: build/generated/ios - React-Core: - :path: "../node_modules/react-native/" - React-CoreModules: - :path: "../node_modules/react-native/React/CoreModules" - React-cxxreact: - :path: "../node_modules/react-native/ReactCommon/cxxreact" - React-debug: - :path: "../node_modules/react-native/ReactCommon/react/debug" - React-Fabric: - :path: "../node_modules/react-native/ReactCommon" - React-FabricImage: - :path: "../node_modules/react-native/ReactCommon" - React-graphics: - :path: "../node_modules/react-native/ReactCommon/react/renderer/graphics" - React-hermes: - :path: "../node_modules/react-native/ReactCommon/hermes" - React-ImageManager: - :path: "../node_modules/react-native/ReactCommon/react/renderer/imagemanager/platform/ios" - React-jserrorhandler: - :path: "../node_modules/react-native/ReactCommon/jserrorhandler" - React-jsi: - :path: "../node_modules/react-native/ReactCommon/jsi" - React-jsiexecutor: - :path: "../node_modules/react-native/ReactCommon/jsiexecutor" - React-jsinspector: - :path: "../node_modules/react-native/ReactCommon/jsinspector-modern" - React-logger: - :path: "../node_modules/react-native/ReactCommon/logger" - React-Mapbuffer: - :path: "../node_modules/react-native/ReactCommon" - react-native-orientation-locker: - :path: "../node_modules/react-native-orientation-locker" - react-native-safe-area-context: - :path: "../node_modules/react-native-safe-area-context" - React-nativeconfig: - :path: "../node_modules/react-native/ReactCommon" - React-NativeModulesApple: - :path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" - React-perflogger: - :path: "../node_modules/react-native/ReactCommon/reactperflogger" - React-RCTActionSheet: - :path: "../node_modules/react-native/Libraries/ActionSheetIOS" - React-RCTAnimation: - :path: "../node_modules/react-native/Libraries/NativeAnimation" - React-RCTAppDelegate: - :path: "../node_modules/react-native/Libraries/AppDelegate" - React-RCTBlob: - :path: "../node_modules/react-native/Libraries/Blob" - React-RCTFabric: - :path: "../node_modules/react-native/React" - React-RCTImage: - :path: "../node_modules/react-native/Libraries/Image" - React-RCTLinking: - :path: "../node_modules/react-native/Libraries/LinkingIOS" - React-RCTNetwork: - :path: "../node_modules/react-native/Libraries/Network" - React-RCTSettings: - :path: "../node_modules/react-native/Libraries/Settings" - React-RCTText: - :path: "../node_modules/react-native/Libraries/Text" - React-rendererdebug: - :path: "../node_modules/react-native/ReactCommon/react/renderer/debug" - React-rncore: - :path: "../node_modules/react-native/ReactCommon" - React-runtimeexecutor: - :path: "../node_modules/react-native/ReactCommon/runtimeexecutor" - React-runtimescheduler: - :path: "../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler" - React-utils: - :path: "../node_modules/react-native/ReactCommon/react/utils" - ReactCommon: - :path: "../node_modules/react-native/ReactCommon" - RNBitmovinPlayer: - :path: "../.." - RNCPicker: - :path: "../node_modules/@react-native-picker/picker" - RNScreens: - :path: "../node_modules/react-native-screens" - Yoga: - :path: "../node_modules/react-native/ReactCommon/yoga" - -SPEC CHECKSUMS: - BitmovinAnalyticsCollector: d08e0b13bcc32973370e0d71f2faa739561bac0a - BitmovinPlayer: 4dd87b63c192ceaa4b29db93a7c7430eece618dc - BitmovinPlayerCore: 63641d00a689efbca6fe97bb8f68aea91f303758 - boost: 88202336c3ba1e7a264a83c0c888784b0f360c28 - DoubleConversion: 74cb0ce4de271b23e772567504735c87134edf0a - FBLazyVector: 33a271a7e8de0bd321e47356d8bc3b2d5fb9ddba - FBReactNativeSpec: 55b7e93b71f300a051190f63c2afeccd839b7e9a - fmt: 745abaaffe4da13101ae15d70dc68ec3d6a666a2 - glog: f0ddebfc00a905e9213e37801095a0a705d2e5f6 - google-cast-sdk: beb7a3ec3def0e3beb618a849b0f1ab2b15b5ebb - GoogleAds-IMA-iOS-SDK: ee2a68ed7a1a17c7bb81bdb1b81590b35a3fc8f3 - GoogleAds-IMA-tvOS-SDK: 9f79f4adcccd2393a93b00f62f566a69175f3978 - hermes-engine: e7981489a718dff7c3a2dacd6302b8761375928d - libevent: a6d75fcd7be07cbc5070300ea8dbc8d55dfab88e - Protobuf: f1b82a3ffb1c8f13d20e141a838c411840e1c223 - RCT-Folly: 46220aef278c0f21b248ba3d60d26d2f64bb36e9 - RCTRequired: 013247b5dbfcf0d918480c9282ed9aa4a142f115 - RCTTypeSafety: 74a07efe760f43e2725acdde03c37ef98dfa02f6 - React: 17bcee9f494516ad89d5b73105900fa55a90d01e - React-callinvoker: 8ba2e4508fae1dc1ee3aaca3b46655c7395586ef - React-Codegen: e51e295fcd666fba3c86ee28b85c30b5efb4a111 - React-Core: 97c42e1cb893f6a35a58d78df229746d64a5e6cb - React-CoreModules: a8e289de1a9c867259e8ca1b306011c9f83258a4 - React-cxxreact: 18611696b738655d165d1c3d62e93913a7c774e8 - React-debug: 1a572b3508dffb5955e29efa22aa8fc4dfc28d51 - React-Fabric: 4bf8d93633f9984ccd34ae20ffe845592e627cfb - React-FabricImage: dec8a2ba57669f9cdbf9663ba4c4a97f2de03061 - React-graphics: 58fcf6a6e73f6ca31f20d55f7a4824015aea1949 - React-hermes: 90717b9b35a745af2e09cb6192b9fbbec904c023 - React-ImageManager: bfcc7a375c3587574f5be0dd3e5cf3f7103dd965 - React-jserrorhandler: 6c281d4ba0a5b5742a4e93c6305e2171cc309372 - React-jsi: 75b0006ae75b9cf7ee35147bae895b4bf4151920 - React-jsiexecutor: 183369bd0a5ec1ec8af33fa51e23e3a6fb0b5309 - React-jsinspector: f881b3aef6261c0a5408e93bb7356485ad35df1b - React-logger: cd57800c6e2eca2d79083f0db1069123b2c69bbd - React-Mapbuffer: 5ea1a30816b7417627dcecceb73110b2f78751f7 - react-native-orientation-locker: 4409c5b12b65f942e75449872b4f078b6f27af81 - react-native-safe-area-context: b97eb6f9e3b7f437806c2ce5983f479f8eb5de4b - React-nativeconfig: 766e5c3efebcffc1968133686f41e429060bbb2f - React-NativeModulesApple: 8360d1cecf8ecade950c8b2ecef918065ee5f862 - React-perflogger: 66eeb4ca0b84466a1258e743a44e51ce9e19cc01 - React-RCTActionSheet: 862261a0337c88f119a59c0abb88f2ebde0592d6 - React-RCTAnimation: 0e5ed8eba63f96efab7771c3417d730354dfbc25 - React-RCTAppDelegate: 1c7d5f83c44363552d88985b8948b50ca6fbcd7f - React-RCTBlob: ae92796821ae69bdd7088b18bd4786bce144957f - React-RCTFabric: 56d2b6aa3718f134788183ae08dee7f84180c0cc - React-RCTImage: a3df84720fd23be9588cfa6e3531b15476ccc5dc - React-RCTLinking: 06976b876f63192bb541f2402bb4e83d3c45127c - React-RCTNetwork: 19b935a236ccf065bb3868508522db7c71dce8f1 - React-RCTSettings: a1e603974937513e719827981b951069197955a0 - React-RCTText: 2e1d00b035039df262c9bfa3064d7069c80c9506 - React-rendererdebug: 3d16bad18e23261a6e80d76a93f957b93ba89f4b - React-rncore: bd4c5f18a58294748ec36851467c05b710c3f6cb - React-runtimeexecutor: 8f0c2b22486f0917a9137b59e577a732eee192a3 - React-runtimescheduler: 20b2202e3396589a71069d12ae9f328949c7c7b8 - React-utils: 0307d396f233e47a167b5aaf045b0e4e1dc19d74 - ReactCommon: 17891ca337bfa5a7263649b09f27a8c664537bf2 - RNBitmovinPlayer: 7a9b1b5e67cb39c9c2a35549ec5616e519a30291 - RNCPicker: b18aaf30df596e9b1738e7c1f9ee55402a229dca - RNScreens: b582cb834dc4133307562e930e8fa914b8c04ef2 - SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 - Yoga: ab50eb8f7fcf1b36aad1801b5687b66b2c0aa000 - -PODFILE CHECKSUM: 11ac6cb62c1978622f6d687b574d9de3441a2680 - -COCOAPODS: 1.16.2 diff --git a/example/metro.config.js b/example/metro.config.js index af10bab5..5deb084c 100644 --- a/example/metro.config.js +++ b/example/metro.config.js @@ -1,49 +1,34 @@ -const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); +// Learn more https://docs.expo.io/guides/customizing-metro +const { getDefaultConfig } = require('expo/metro-config'); const path = require('path'); -const exclusionList = require('metro-config/src/defaults/exclusionList'); -const escape = require('escape-string-regexp'); -const pak = require('../package.json'); -const root = path.resolve(__dirname, '..'); +const config = getDefaultConfig(__dirname); -const modules = Object.keys({ - ...pak.peerDependencies, -}); +// npm v7+ will install ../node_modules/react and ../node_modules/react-native because of peerDependencies. +// To prevent the incompatible react-native between ./node_modules/react-native and ../node_modules/react-native, +// excludes the one from the parent folder when bundling. +config.resolver.blockList = [ + ...Array.from(config.resolver.blockList ?? []), + new RegExp(path.resolve('..', 'node_modules', 'react')), + new RegExp(path.resolve('..', 'node_modules', 'react-native')), +]; -/** - * Metro configuration - * https://facebook.github.io/metro/docs/configuration - * - * @type {import('metro-config').MetroConfig} - */ -const config = { - projectRoot: __dirname, - watchFolders: [root], +config.resolver.nodeModulesPaths = [ + path.resolve(__dirname, './node_modules'), + path.resolve(__dirname, '../node_modules'), +]; - // We need to make sure that only one version is loaded for peerDependencies - // So we exclusionList them at the root, and alias them to the versions in example's node_modules - resolver: { - blockList: exclusionList( - modules.map( - (m) => - new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) - ) - ), +config.resolver.extraNodeModules = { + 'bitmovin-player-react-native': '..', +}; - extraNodeModules: modules.reduce((acc, name) => { - acc[name] = path.join(__dirname, 'node_modules', name); - return acc; - }, {}), - }, +config.watchFolders = [path.resolve(__dirname, '..')]; - transformer: { - getTransformOptions: async () => ({ - transform: { - experimentalImportSupport: false, - inlineRequires: true, - }, - }), +config.transformer.getTransformOptions = async () => ({ + transform: { + experimentalImportSupport: false, + inlineRequires: true, }, -}; +}); -module.exports = mergeConfig(getDefaultConfig(__dirname), config); +module.exports = config; diff --git a/example/package.json b/example/package.json index 2bb68c46..a50492ab 100644 --- a/example/package.json +++ b/example/package.json @@ -1,41 +1,85 @@ { - "private": true, - "name": "bitmovin-player-react-native-sample", - "description": "Example app showcasing Bitmovin's React Native Player SDK", - "version": "0.0.1", + "name": "bitmovin-player-react-native-example", + "license": "0BSD", + "version": "1.0.0", + "main": "index.ts", "scripts": { - "postinstall": "patch-package", - "android": "react-native run-android", - "ios": "react-native run-ios", - "start": "react-native start", + "bootstrap": "yarn install && yarn prebuild && yarn pods", + "open:android": "open -a \"Android Studio\" android", + "open:ios": "xed ios", "pods": "yarn pods-install || yarn pods-update", - "pods-install": "[ \"$(uname)\" != Darwin ] || NO_FLIPPER=1 yarn pod-install", - "pods-update": "[ \"$(uname)\" != Darwin ] || cd ios && NO_FLIPPER=1 pod update --silent" + "pods-install": "[ \"$(uname)\" != Darwin ] || yarn pod-install", + "pods-update": "[ \"$(uname)\" != Darwin ] || cd ios && pod update --silent", + "prebuild": "expo prebuild", + "prebuild:tv": "EXPO_TV=1 expo prebuild", + "run:android": "expo run:android", + "run:android-tv": "EXPO_TV=1 expo run:android", + "run:ios": "expo run:ios", + "run:tvos": "EXPO_TV=1 expo run:ios", + "start": "expo start", + "start:log": "rm -f expo.log && script -q -F expo.log bash -c 'expo start'", + "logs": "tail -n ${LINES:-30} expo.log", + "build:ios": "./scripts/build-ios.sh", + "build:tvos": "./scripts/build-tvos.sh", + "build:android": "cd android && ./gradlew assembleDebug --quiet --console=plain --warning-mode=none", + "build:android-tv": "EXPO_TV=1 yarn build:android", + "build:ts": "tsc --noEmit", + "lint": "eslint src --ext .ts,.tsx,.js,.jsx --quiet", + "typecheck": "tsc --noEmit", + "android": "yarn prebuild && yarn run:android", + "ios": "yarn prebuild && yarn run:ios", + "tvos": "yarn prebuild:tv && yarn run:tvos", + "android-tv": "yarn prebuild:tv && yarn run:android-tv" }, "dependencies": { - "@react-native-picker/picker": "2.6.1", - "@react-navigation/elements": "1.3.26", + "@react-native-tvos/config-tv": "^0.1.1", + "@react-navigation/elements": "1.3.30", "@react-navigation/native": "6.1.14", "@react-navigation/native-stack": "6.9.22", - "react": "18.2.0", - "react-native": "npm:react-native-tvos@0.73.4-0", - "react-native-modal": "13.0.1", - "react-native-orientation-locker": "1.6.0", - "react-native-safe-area-context": "4.9.0", - "react-native-screens": "3.29.0", + "expo": "53.0.20", + "expo-build-properties": "~0.14.8", + "expo-crypto": "~14.1.5", + "expo-dev-client": "~5.2.4", + "expo-device": "~7.1.4", + "expo-notifications": "~0.31.4", + "expo-screen-orientation": "~8.1.7", + "expo-system-ui": "~5.0.10", + "react": "19.0.0", + "react-native": "npm:react-native-tvos@0.79.5-0", + "react-native-safe-area-context": "5.4.0", + "react-native-screens": "~4.11.1", "react-native-system-navigation-bar": "^2.6.4" }, "devDependencies": { - "@babel/core": "^7.20.0", - "@babel/preset-env": "^7.20.0", - "@babel/runtime": "^7.20.0", - "@react-native/babel-preset": "0.73.18", - "@react-native/metro-config": "^0.73.5", - "babel-plugin-module-resolver": "^5.0.0", - "patch-package": "^8.0.0", - "pod-install": "^0.1.0" + "@babel/core": "^7.28.0", + "@expo/cli": "^0.24.20", + "@expo/config-types": "^53.0.5", + "@types/react": "~19.0.10", + "dotenv": "^17.2.0", + "eslint": "^8.57.0", + "eslint-config-expo": "~9.2.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.5.1", + "pod-install": "^0.3.10", + "prettier": "^3.6.2", + "typescript": "~5.8.3" }, - "engines": { - "node": ">=18" + "private": true, + "expo": { + "install": { + "exclude": [ + "react-native" + ] + }, + "autolinking": { + "nativeModulesDir": ".." + }, + "doctor": { + "reactNativeDirectoryCheck": { + "exclude": [ + "@react-native-tvos/config-tv" + ] + } + } } } diff --git a/example/prettier.config.js b/example/prettier.config.js new file mode 100644 index 00000000..972157f7 --- /dev/null +++ b/example/prettier.config.js @@ -0,0 +1,7 @@ +module.exports = { + quoteProps: 'consistent', + singleQuote: true, + tabWidth: 2, + trailingComma: 'es5', + useTabs: false, +}; diff --git a/example/react-native.config.js b/example/react-native.config.js deleted file mode 100644 index 521fccd1..00000000 --- a/example/react-native.config.js +++ /dev/null @@ -1,13 +0,0 @@ -const path = require('path'); -const pak = require('../package.json'); - -module.exports = { - dependencies: { - ...(process.env.NO_FLIPPER - ? { 'react-native-flipper': { platforms: { ios: null, android: null } } } - : {}), - [pak.name]: { - root: path.join(__dirname, '..'), - }, - }, -}; diff --git a/example/scripts/build-ios.sh b/example/scripts/build-ios.sh new file mode 100755 index 00000000..2898b914 --- /dev/null +++ b/example/scripts/build-ios.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# Build iOS project for simulator with formatted output + +set -o pipefail +set -e + +XCBEAUTIFY_ARGS=$@ + +eval "xcodebuild -workspace ios/BitmovinPlayerReactNativeExample.xcworkspace \ + -scheme BitmovinPlayerReactNativeExample \ + -configuration Debug \ + -quiet \ + ${XCODEBUILD_ARGS} \ + | xcbeautify -qq --disable-logging $XCBEAUTIFY_ARGS" diff --git a/example/scripts/build-tvos.sh b/example/scripts/build-tvos.sh new file mode 100755 index 00000000..093379e3 --- /dev/null +++ b/example/scripts/build-tvos.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# Build tvOS project for simulator with formatted output + +set -o pipefail +set -e + +XCBEAUTIFY_ARGS=$@ + +eval "xcodebuild -workspace ios/BitmovinPlayerReactNativeExample.xcworkspace \ + -scheme BitmovinPlayerReactNativeExample \ + -configuration Debug \ + -quiet \ + ${XCODEBUILD_ARGS} \ + | xcbeautify -qq --disable-logging $XCBEAUTIFY_ARGS" diff --git a/example/src/App.tsx b/example/src/App.tsx index 2e10c807..a686d50a 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -17,11 +17,15 @@ import CustomPlayback from './screens/CustomPlayback'; import BasicPictureInPicture from './screens/BasicPictureInPicture'; import CustomHtmlUi from './screens/CustomHtmlUi'; import BasicFullscreenHandling from './screens/BasicFullscreenHandling'; -import LandscapeFullscreenHandling from './screens/LandscapeFullscreenHandling'; +// Import LandscapeFullscreenHandling only on non-TV platforms +const LandscapeFullscreenHandling = Platform.isTV + ? () => null + : require('./screens/LandscapeFullscreenHandling').default; import SystemUI from './screens/SystemUi'; import OfflinePlayback from './screens/OfflinePlayback'; import Casting from './screens/Casting'; import BackgroundPlayback from './screens/BackgroundPlayback'; +import * as Device from 'expo-device'; export type RootStackParamsList = { ExamplesList: { @@ -68,12 +72,14 @@ const RootStack = createNativeStackNavigator(); const isTVOS = Platform.OS === 'ios' && Platform.isTV; const isAndroidTV = Platform.OS === 'android' && Platform.isTV; +const isIOSSimulator = Device.osName === 'iOS' && Device.isDevice === false; +const isTVOSSimulator = Device.osName === 'tvOS' && Device.isDevice === false; export default function App() { useEffect(() => { // iOS audio session category must be set to `playback` first, otherwise playback // will have no audio when the device is silenced. - // This is also required to make Picture in Picture work on iOS. + // This is also required to make Picture in Picture work on iOS and tvOS. // // Usually it's desireable to set the audio's category only once during your app's main component // initialization. This way you can guarantee that your app's audio category is properly @@ -102,10 +108,6 @@ export default function App() { title: 'Subtitle and captions', routeName: 'SubtitlePlayback' as keyof RootStackParamsList, }, - { - title: 'Basic Picture in Picture', - routeName: 'BasicPictureInPicture' as keyof RootStackParamsList, - }, { title: 'Basic Ads', routeName: 'BasicAds' as keyof RootStackParamsList, @@ -134,9 +136,18 @@ export default function App() { routeName: 'CustomHtmlUi', }); + if (!isIOSSimulator) { + stackParams.data.push({ + title: 'Offline playback', + routeName: 'OfflinePlayback', + }); + } + } + + if (!isTVOSSimulator && !isIOSSimulator) { stackParams.data.push({ - title: 'Offline playback', - routeName: 'OfflinePlayback', + title: 'Basic Picture in Picture', + routeName: 'BasicPictureInPicture' as keyof RootStackParamsList, }); } @@ -176,14 +187,16 @@ export default function App() { options={({ navigation }) => ({ title: 'Examples', // eslint-disable-next-line react/no-unstable-nested-components - headerRight: () => ( -