npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tivio/sdk-react

v9.7.1

Published

@tivio/sdk-react provides everything which is necessary (components, data hooks etc.) to build a custom React application above Tivio Studio. You can comfortably manage all you videos, settings such as application's screens and rows and also monetization

Downloads

608

Readme

@tivio/sdk-react

@tivio/sdk-react provides everything which is necessary (components, data hooks etc.) to build a custom React application above Tivio Studio. You can comfortably manage all you videos, settings such as application's screens and rows and also monetization settings in the administration of Tivio Studio while having the freedom to build your own application.

Installation

Install @tivio/sdk-react along with its peer dependencies

npm i react@17 react-dom@17 @tivio/sdk-react
# or
yarn add react@17 react-dom@17 @tivio/sdk-react

Initialization

Put Tivio Provider at the top level of your application:

import { TivioProvider } from '@tivio/sdk-react'

const config = {
    secret: 'XXXXXXXXXX',
}

function App({children}) {
    return (
        <TivioProvider conf={config}>
           {children}
        </TivioProvider>
    )
}

Authentication

A logged-in user can access more features, such as making purchases. In order to authenticate a user with Tivio, use the setUser() method.

Verification of the user against 3rd party auth servers is implemented per customer.

import { setUser } from '@tivio/sdk-react'

// Log in

// Payload is specific per customer
// A common use-case is sending an auth token inside the payload, which Tivio can use to verify the user
await setUser('userId', { token: 'xxx'})

// Log out
await setUser(null)

Single Sign-On (SSO) across subdomains

If you want to enable Single Sign-On (SSO) for all subdomains under the same parent domain, you can specify the customTokenCookiesDomain in the configuration object.

import type { Config } from '@tivio/sdk-react'

const config: Config = {
  // ... other required tivio config properties
  customTokenCookiesDomain: 'example.com' // allows SSO across example.com, app.example.com, admin.example.com, etc.
};

Create user with email and password

Returns user's firebase uid or null if error occurs

tivio.createUserWithEmailAndPassword(email: string, password: string, username: string, phoneNumber?: string | undefined): Promise<string | null>

Example

await tivio.createUserWithEmailAndPassword('[email protected]', 'password', 'Fist & Last Name', '+420777123456')

Sign in with email and password

Returns user's firebase uid or null if error occurs

tivio.signInWithEmailAndPassword(email: string, password: string): Promise<string | null>

Example

await tivio.signInWithEmailAndPassword('[email protected]', 'password')

Sign out

tivio.signOut(): Promise<void>

Reset password

Send email with password reset link to the user with given email address

tivio.resetPassword(email: string): Promise<void>

User entity

Get user

Returns user object or null if user is not authenticated

tivio.getUser(): Promise<User | null>

Essential Properties

Basic Information

  • id: string - Unique Tivio user ID
  • email?: string - Email address
  • name?: string - Display name - could be nickname, first name and last name, etc.

Authentication Status

  • isSignedIn: boolean - Whether the user is currently signed in
  • isReady: boolean - Whether user data is fully loaded
  • isAnonymous: boolean - Whether this is an anonymous user

Content & Preferences

  • purchases: Purchase[] - Active purchases (excluding vouchers)

User Profiles

  • profiles: UserProfile[] - Available user profiles (e.g. multiple people can use the same user account with different preferences)
  • activeUserProfileId: string | null - Currently active profile

Essential Methods

Authentication

changePassword(oldPassword: string, newPassword: string): Promise<void>

Profile Management

Create user profile

interface CreateUserProfileRequest {
    /**
     * Profile name - typically first name and last name, but users can fill in whatever they want.
     */
    name: string
    /**
     * Filled in values for user profile survey. See {@link OrganizationDocument} its configuration.
     */
    survey?: ProfileSurvey
}
interface ProfileSurvey {
    gender?: Translation
    age?: AgeRange
}
interface AgeRange {
    from: number
    /**
     * If not set, we assume the range is not closed and {@property to} is set to infinity
     * (there are no values to represent {@link Infinity} in firestore, so we use undefined instead).
     */
    to?: number
    /**
     * If set, we assume that this is a profile for kids only (e.g. 0-12).
     * This value can only be `true` or not specified (undefined).
     */
    kidsOnly?: true
}
createUserProfile(request: CreateUserProfileRequest): Promise<void>

Example

await user.createUserProfile({
    name: 'John Doe',
    survey: {
        gender: {
            cs: "Žena",
            en: "Female",
            sk: "Žena"
        },
        age: {
            from: 18,
            to: 24
        }
    }
})

Set active user profile

user.setActiveUserProfileId(profileId: string): void

Delete user profile

user.deleteUserProfile(profileId: string): Promise<void>

Content

Assets

In order to obtain assets (images) from a Video or Tag, you can use these methods:

Video or Tag

  • video.cover - cover image (landscape)
  • video.banner - banner image
  • video.circled - circled image
  • video.detailBanner - detail banner image (landscape)
  • video.portrait - portrait image (portrait)

Tag only

  • tag.bannerMobile - banner image mobile

All of the assets fallback to type cover or empty string if cover is not available

Get content based on user profile

interface GetUserProfileDataOptions {
    /**
     * If true, the data will be returned for all user profiles.
     * If false, the data will be returned only for the active user profile.
     */
    ignoreActiveUserProfile?: boolean
}

getFavorites(options?: GetUserProfileDataOptions): Promise<FavoriteWithData[]>
getWatchPositions(options?: GetUserProfileDataOptions): Promise<WatchPositionWithData[]>
getWatchHistory(options?: GetUserProfileDataOptions): Promise<WatchPositionWithData[]>

Get user favorites

interface FavoriteWithData {
    content: Video | Tag
    type: 'video' | 'tag' // Filtering by this type will set the content type to be Video or Tag
    profileId?: string
}

getFavorites(options?: GetUserProfileDataOptions): Promise<FavoriteWithData[]>

Example

const favorites = await user.getFavorites()
favorites.forEach(favorite => {
    console.log({
        name: favorite.name,
        cover: favorite.cover, // cover image (landscape)
        portrait: favorite.portrait, // portrait image (portrait)
    })
})

Get user watch positions

interface WatchPositionWithData {
    position: number
    video: Video
    tag?: Tag
    episodeNumber?: number
    seasonNumber?: number
    videoDuration?: number
}

getWatchPositions(options?: GetUserProfileDataOptions): Promise<WatchPositionWithData[]>

Example

const watchPositions = await user.getWatchPositions()
watchPositions.forEach(watchPosition => {
    console.log({
        position: watchPosition.position, // watch position in milliseconds
        videoName: watchPosition.video.name,
        videoCover: watchPosition.video.cover,
        tagName: watchPosition.tag?.name, // optional tag for series
        episodeNumber: watchPosition.episodeNumber, // optional episode number
        seasonNumber: watchPosition.seasonNumber, // optional season number
        videoDuration: watchPosition.videoDuration, // optional video duration
    })
})

Get user watch history

getWatchHistory(options?: GetUserProfileDataOptions): Promise<WatchPositionWithData[]>

Example

const watchHistory = await user.getWatchHistory()
watchHistory.forEach(watchPosition => {
    console.log({
        position: watchPosition.position, // watch position in milliseconds
        videoName: watchPosition.video.name,
        videoCover: watchPosition.video.cover,
        tagName: watchPosition.tag?.name, // optional tag for series
        episodeNumber: watchPosition.episodeNumber, // optional episode number
        seasonNumber: watchPosition.seasonNumber, // optional season number
        videoDuration: watchPosition.videoDuration, // optional video duration
    })
})

Add to/remove from favorites

Simply call addToFavorites or removeFromFavorites on the Video or Tag.

const video = await tivio.getVideoById('videoId')
await video.addToFavorites()
await video.removeFromFavorites()

const tag = await tivio.getTagById('tagId')
await tag.addToFavorites()
await tag.removeFromFavorites()

// Remove a favorite from favorites
const favorites = await tivio.getUser()?.favorites
favorites[0]?.removeFromFavorites()

ℹ️ Note: When user saves favorite without profileId, it will only be shown if the app doesn't have any active user profile.

Get screen by ID

getScreenById(screenId: string): Promise<Screen | null>

Get row by ID

getRowById(rowId: string): Promise<Row | null>

Get video by ID

getVideoById(videoId: string): Promise<Video | null>

Get tag by ID

getTagById(tagId: string): Promise<Tag | null>

Get TV Channel by ID

getTvChannelById(tvChannelId: string): Promise<TvChannel | null>

Player

You can choose whether you will use complete player component provided by Tivio or you will wrap your existing player with the Tivio Player Wrapper.

Tivio Player component

import { useTivioData } from '@tivio/sdk-react'

const PlayerExample = () => {
    const bundle = useTivioData()

    if (!bundle?.components?.WebPlayer) {
        return <p>Loading...</p>
    }

    return (
        <>
            <div
                style={{
                    height: 720,
                    width: 1280,
                }}
            >
                <bundle.components.WebPlayer
                    id="player1"
                    className="web-player"
                    source="/videos/xxxxxxxxxxxxxxxxxxxx" // dynamically change this based on video you want to play
                />
            </div>
        </>
    )
}

Player wrapper

Player wrapper is the way how you can enhance your video player with Tivio features, such Tivio Ads. In order to start using Tivio player wrapper, wrap your player methods with PlayerWrapper, start using PlayerWrapper's methods instead of them to control your playback and start sending player events to Tivio PlayerWrapper.

Wrap your player methods with Tivio player wrapper

import { useTivioReadyData } from '@tivio/sdk-react'

function CustomPlayer() {
    const tivioData = useTivioReadyData()

    useEffect(() => {
        if (tivioData) {
            // If your app uses multiple player instances, use different Tivio player wrapper for each
            // distinguished by playerWrapperId
            const playerWrapper = tivio.getPlayerWrapper({ playerWrapperId: 'PLAYER_1' })

            // Pass your player methods to Tivio player wrapper
            playerWrapper.register({
                play: () => {
                    // Un-pause your player
                },
                pause: () => {
                    // Pause your player
                },
                seekTo: (ms: number) => {
                    // Seek to position in milliseconds using your player
                },
                setSource: (videoSource: InputSource) => {
                    // Send this video source to your player to load it
                },
            })
        }
    }, [tivioData])
}

Start using Tivio player wrapper methods to control playback

    // Channel source metadata, such as channel name, epg start and epg end are necessary
    // for TV ad segment detection and application of ad strategies
    const source = new ChannelSource(
        'https://channel_prima_hd.m3u8',
        {
            // here put any additional metadata, for your use.
            // This object will not be touched by Tivio
        },
        // channel name
        // can also be prima hd, prima_hd, prima, Prima, PRIMA, etc.
        // we will normalize it to snake case and add '_hd' if necessary
        'Prima HD',
        // program name
        'Dr. House',
        // description (optional)
        'Episode about Dr. House being awesome',
        // EPG start
        new Date('2021-12-10T12:00:00'),
        // EPG end
        new Date('2021-12-10T13:40:00'),
    )

    // Send source to player
    playerWrapper.onSourceChanged(source)

    // Un-pause player
    playerWrapper.play()

    // Pause player
    playerWrapper.pause()
}

Start reporting player events to Tivio

    // Report that source is playing
    playerWrapper.onStateChanged('playing')

    // Report that source is paused
    playerWrapper.onStateChanged('paused')

    // Report that source stopped playing
    playerWrapper.onPlaybackEnded()
    playerWrapper.onStateChanged('idle')

    // Report video progress
    playerWrapper.onTimeChanged(ms)
}

Start reporting playback-related errors to Tivio

    // Report that video failed to load (e.g. due to a wrong URI)
    playerWrapper.onLoadError(new Error('video failed to load'))

    // Report that video failed during playback (e.g. due to bad connection, corrupted chunks of stream video etc.)
    // This type of error may be auto-recoverable
    playerWrapper.onError(new Error('playback error'))
}

JavaScript Methods for Rendering Players

renderWebPlayer

The renderWebPlayer method allows you to render a Tivio player outside of React components. This is useful for scenarios where you need to integrate the player into non-React environments.

The method is asynchronous and returns a VideoController instance that provides comprehensive control over playback.

import { renderWebPlayer } from '@tivio/sdk-react'

// Example usage
const videoController = await renderWebPlayer(
    document.getElementById('video-player'),
    {
        id: 'player-main',
        source: 'videos/ID',
    }
)

// Control playback
videoController.play()
videoController.pause()
videoController.togglePause()

// Seeking
videoController.seekToAbsolutePosition(30000) // Seek to 30 seconds
videoController.seekBy(5000) // Seek forward by 5 seconds
videoController.seekToPercent(50) // Seek to 50% of video
videoController.seekToLive() // Seek to live position (for live streams)

// Volume control
videoController.changeVolume(0.8) // Set volume to 80%
videoController.volumeUp() // Increase volume by 5%
videoController.volumeDown() // Decrease volume by 5%
videoController.mute()
videoController.unmute()
videoController.toggleMute()

// Get current source
console.log('Current source:', videoController.currentSource)

// Change source
videoController.setSource('videos/newVideoId') // Change to a different video
videoController.setSource('tvChannels/channelId') // Change to a TV channel
videoController.setSource(null) // Stop playback

// Event handling
videoController.addEventListener('video_unit_ended', () => {
    console.log('Video playback ended')
})

videoController.addEventListener('timeupdate', (currentTime) => {
    console.log('Current time:', currentTime)
})

// Cleanup
videoController.destroy()

VideoController Methods

The VideoController returned by renderWebPlayer provides the following methods:

Playback Control:

  • play() - Resume playback from paused state
  • pause() - Pause the current playback
  • togglePause() - Toggle between play and pause states
  • replay() - Replay the current video from the beginning
  • retry() - Retry playback if it failed to start

Seeking:

  • seekToAbsolutePosition(ms: number) - Seek to absolute position in milliseconds
  • seekBy(ms: number) - Seek relative to current position
  • seekToPercent(percentage: number) - Seek to percentage of video duration
  • seekToLive() - Seek to live position (for live streams)

Volume Control:

  • changeVolume(value: number) - Set volume (0-1)
  • volumeUp() - Increase volume by 5%
  • volumeDown() - Decrease volume by 5%
  • mute() - Mute audio
  • unmute() - Unmute audio
  • toggleMute() - Toggle mute state

Source Management:

  • setSource(source: WebPlayerProps['source']) - Change the current source to a new one (updates player props)
  • currentSource - Currently playing source or null if no source is loaded

Source Types

The Tivio player supports different types of sources:

PathSourceParams - Path-based sources with ad configuration

  • Used for playing Tivio-hosted videos and TV channels with custom VAST ad configurations
  • Supports both videos/ID and tvChannels/ID paths
  • Includes staticAdsConfig for custom VAST ad insertion
  • Example usage:
const sourceWithAds = {
    path: 'videos/123', // or 'tvChannels/456'
    staticAdsConfig: [
        {
            type: 'preroll',
            url: 'https://example.com/vast-preroll-ad.xml',
        },
        {
            type: 'midroll',
            from: 30000, // 30 seconds
            url: 'https://example.com/vast-midroll-ad.xml',
        },
        {
            type: 'postroll',
            url: 'https://example.com/vast-postroll-ad.xml',
        },
    ],
}

VOD_TIVIO - Tivio-hosted video-on-demand content

  • Used for playing videos that are hosted within Tivio's infrastructure
  • Example usage:
const vodSource = {
    type: SourceType.VOD_TIVIO,
    videoPath: 'videos/123', // Video path
    sourcePlayMode: SourcePlayMode.ON_DEMAND,
    name: 'Tivio Video',
    autoplay: false,
    continuePositionMs: 10000, // Start at 10 seconds (optional)
}

CHANNEL - TV channel content (both classic and virtual channels)

  • Used for playing live TV channels
  • Example usage:
const channelSource = {
    type: SourceType.CHANNEL,
    path: 'tvChannels/456', // TV channel path
    sourcePlayMode: SourcePlayMode.LIVE,
    name: 'TV Channel',
    autoplay: true,
}

VOD_EXTERNAL - External video-on-demand content from third-party URLs

  • Used for playing videos that are hosted outside of Tivio's infrastructure
  • Supports various streaming protocols (HLS, DASH, MP4)
  • Optionally, you can include staticAdsConfig for custom ad insertion
  • Example usage (single url):
const externalSource = {
    type: SourceType.VOD_EXTERNAL,
    url: 'https://example.com/video.m3u8',
    sourcePlayMode: SourcePlayMode.ON_DEMAND,
    poster: 'https://example.com/poster.jpg',
    name: 'External Video',
    autoplay: false,
    continuePositionMs: 10000, // Start at 10 seconds (optional)
    staticAdsConfig: [
        {
            type: 'preroll',
            url: 'https://example.com/preroll-ad.xml',
        },
        {
            type: 'midroll',
            from: 20000, // 20 seconds
            url: 'https://example.com/midroll-ad.xml',
        },
    ],
}

Path-Based Sources You can also use simple path strings for both video and TV channel sources:

// Video path
videoController.setSource('videos/123')

// TV channel path  
videoController.setSource('tvChannels/456')

Static Ads Configuration

ℹ️ Note: To enable ad functionality, you must configure the IMA ad service in your Tivio configuration:

import { AD_SERVICE_PROXY_NAME } from '@tivio/sdk-react'

const config = {
    // ... other config properties
    player: {
        adService: {
            name: AD_SERVICE_PROXY_NAME.IMA,
        },
    },
}

The staticAdsConfig property lets you specify custom ad insertion points within your content. If multiple ads are set for the same entry point, they will be played one after another in sequence. For example, if you have two preroll ads, the first will play, followed by the second. Similarly, midroll ads that share the same from time will be grouped and played sequentially.

It supports the following ad types:

Preroll Ads - Play before the main content starts

{
    type: 'preroll',
    url: 'https://example.com/preroll-ad.xml',
}

Midroll Ads - Play during the main content at specified time

{
    type: 'midroll',
    from: 30000, // Time in milliseconds (30 seconds)
    url: 'https://example.com/midroll-ad.xml',
}

Postroll Ads - Play after the main content ends

{
    type: 'postroll',
    url: 'https://example.com/postroll-ad.xml',
}

Complete Example:

const sourceWithAds = {
    path: 'videos/123',
    name: 'Video with Multiple Ad Types',
    staticAdsConfig: [
        // multiple preroll ads
        {
            type: 'preroll',
            url: 'https://vasterix.joj.sk/api/v1/creative?id=0c5d96fd-2ab9-4207-a325-4607437965e3&vast=4.0',
        },
        {
            type: 'preroll',
            url: 'https://example.com/preroll-ad.xml',
        },
        // multiple midroll ads (with same from time)
        {
            type: 'midroll',
            from: 30000, // 30 seconds
            url: 'https://example.com/midroll-ad-1.xml',
        },
        {
            type: 'midroll',
            from: 30000, // 30 seconds
            url: 'https://example.com/midroll-ad-2.xml',
        },
        // one midroll ad with different from time
        {
            type: 'midroll',
            from: 60000, // 1 minute
            url: 'https://example.com/midroll-ad-2.xml',
        },
        // postroll ad
        {
            type: 'postroll',
            url: 'https://example.com/postroll-ad.xml',
        },
    ],
}
  • It is also possible to set multiple urls for external source type, e.g. when you want to use both DASH and HLS formats. Player then automatically will pick most suitable format to play depending on device capabilities.
  • Example usage (multiple urls):
const externalSource = {
    type: SourceType.VOD_EXTERNAL,
    urls: ['https://example.com/video.m3u8', 'https://example.com/video.mpd'],
    sourcePlayMode: SourcePlayMode.ON_DEMAND,
}

VOD_TIVIO - Internal source type for playing content managed by Tivio

  • Used for playing videos and tv channels managed by Tivio infrastructure, e.g. everything that is uploaded through Tivio Admin application
  • For convenience, it is recommended to use shortcut format and pass the source as string in ${'videos' | 'tvChannels'}/{id} format
  • Ids of corresponding videos and tv channel could be found in Tivio admin application
  • Example usage:
const tivioVideoSource = 'videos/2BzH4xsTXW8vqYKJegXj'
const tivioTvChannelSource = 'tvChannels/Ct4UcK6ozX3VfxaL5yP5'
  • Otherwise, it is also possible to pass internal tivio source as an object with type: SourceType.VOD_TIVIO and additional parameters, similarly to external source type.

Source Play Modes

The sourcePlayMode property determines how the content is played:

  • ON_DEMAND - Standard video playback with full seeking capabilities
  • LIVE - Live stream mode with no seeking
  • HYBRID - Combines LIVE with seeking

User Authentication Callbacks

The userAuthCallbacks property allows you to handle user authentication flows when the player requires user login or registration. This is particularly useful for content that requires authentication (e.g., premium content).

interface UserAuthCallbacks {
    onGoToLogin: () => void
    onGoToRegistration: () => void
}

Example Implementation:

// Usage with renderWebPlayer
const videoController = await renderWebPlayer(
    document.getElementById('video-player'),
    {
        id: 'player-main',
        source: 'videos/PREMIUM_VIDEO_ID',
        userAuthCallbacks: {
            onGoToLogin: () => {
                // Show your login modal
                setShowLoginModal(true)
            },
            onGoToRegistration: () => {
                // Show your registration modal
                setShowRegistrationModal(true)
            },
        },
    }
)

// Handle login in your modal
const handleLogin = async (email: string, password: string) => {
    try {
        await tivio.signInWithEmailAndPassword(email, password)
        console.log('Login successful')
    } catch (error) {
        console.error('Login failed:', error)
        throw error
    }
}

When are these callbacks triggered?

  • onGoToLogin: Called when the player is trying to play content behind a paywall and user clicks on the login button in the overlay
  • onGoToRegistration: Called when the player is trying to play content behind a paywall and user clicks on the registration button in the overlay

Using setSource with VideoController

The setSource method allows you to dynamically change what's playing:

// Change to a different video
videoController.setSource('videos/newVideoId')

// Change to a video with custom ads
videoController.setSource({
    path: 'videos/newVideoId',
    name: 'Video with Ads',
    staticAdsConfig: [
        {
            type: 'preroll',
            url: 'https://example.com/preroll-ad.xml',
        },
        {
            type: 'midroll',
            from: 30000,
            url: 'https://example.com/midroll-ad.xml',
        },
    ],
})

// Change to an external video
videoController.setSource({
    type: SourceType.VOD_EXTERNAL,
    url: 'https://example.com/video.mpd',
    sourcePlayMode: SourcePlayMode.ON_DEMAND,
    name: 'External Video'
})

// Change to a TV channel
videoController.setSource('tvChannels/channelId')

// Change to a TV channel with custom ads
videoController.setSource({
    path: 'tvChannels/channelId',
    name: 'TV Channel with Ads',
    staticAdsConfig: [
        {
            type: 'preroll',
            url: 'https://example.com/tv-preroll-ad.xml',
        },
    ],
})

// Stop playback
videoController.setSource(null)

The setSource method is particularly useful for:

  • Play next
  • Implementing playlists
  • Dynamic content loading

Event Handling:

  • addEventListener(event: string, callback: (value: T) => void) - Add event listener
  • removeEventListener(event: string, callback: (value: T) => void) - Remove event listener

Utility:

  • destroy() - Destroy player and clean up resources

Playback Events

The VideoController emits various events that you can listen to:

Ad events

videoController.addEventListener('ad-started', (adMetadata: AdMetadata | null) => {
    if (!adMetadata) {
        console.log('Ad started playing (no metadata available)')
        return
    }

    console.log('Ad started playing', adMetadata)

    if ('customAdMetadata' in adMetadata && adMetadata.customAdMetadata) {
        console.log('Ad custom metadata:', adMetadata.customAdMetadata)
        // customAdMetadata contains VAST trafficking parameters (from VAST AdParameters tag)
    }

    // Access CTA element for rendering custom call-to-action buttons
    const { ctaElement } = adMetadata
    if (ctaElement) {
        console.log('CTA element available for rendering custom buttons', ctaElement)

        const { customAdMetadata } = adMetadata
        if (!customAdMetadata) {
            console.log('No custom ad metadata available')
            return
        }

        const { extensions } = customAdMetadata
        if (!extensions) {
            console.log('No extensions available')
            return
        }

        const { parameters } = extensions[0]
        if (!parameters) {
            console.log('No parameters available')
            return
        }

        const metadataParameters = parameters as {
            main_title?: string
            subtitle?: string
            image?: string
            button_text: string
            url: string
        }

        const buttonText = metadataParameters.button_text as string | undefined

        if (!buttonText) {
            console.log('No button text available')
            return
        }
        
        // Example: Create a custom CTA button using React portal
        const CTAButton = () => (
            <div style={{
                position: 'absolute',
                bottom: '20px',
                right: '20px',
                backgroundColor: 'rgba(0, 0, 0, 0.8)',
                color: 'white',
                padding: '12px 24px',
                borderRadius: '6px',
                cursor: 'pointer',
                fontSize: '16px',
                fontWeight: 'bold',
                pointerEvents: 'auto',
            }}>
                Learn More
            </div>
        )
        
        // Render the CTA button using React portal
        const root = ReactDOM.createRoot(ctaElement)
        root.render(<CTAButton />)
    }

    // adMetadata contains information like:
    // - ctaElement?: HTMLElement (for rendering custom CTA buttons)
    // - customAdMetadata?: Record<string, unknown> (for IMA ads with rich metadata)
    // - type: 'ad'
    // - subType: 'inserted' | 'original'
    // - secondsToEnd: number
    // - secondsToSkippable: number | null
    // - canTriggerSkip: boolean
    // - isSkippable: boolean
    // - order: number | null
    // - totalCount: number | null
    // - skip: () => void
    // Update UI, show ad overlay, etc.
})

videoController.addEventListener('ad-ended', () => {
    console.log('Ad finished playing')
})

// Companion ads event - fires when companion ads are available for the current ad
videoController.addEventListener('companion-ads', (companionAds) => {
    console.log('Companion ads available:', companionAds)
    
    // Access companion ad properties using IMA methods
    companionAds.forEach((companionAd, index) => {
        console.log(`Companion Ad ${index + 1}:`, {
            width: companionAd.getWidth(),
            height: companionAd.getHeight(),
            contentType: companionAd.getContentType(),
            // HTML content as string
            content: companionAd.getContent()
        })
    })
})

ℹ️ Note: The CTA overlay element is visible in the WebPlayer only while an ad is playing and is automatically cleaned up on source changes.

Video state changes

// Video state changes
videoController.addEventListener('statechange', (state) => {
    console.log('Player state:', state) // 'playing', 'paused', 'idle'
})

// Time updates
videoController.addEventListener('timeupdate', (currentTime) => {
    console.log('Current time:', currentTime)
})

// Video ended
videoController.addEventListener('video_unit_ended', () => {
    console.log('Video playback ended')
})

// Duration changes
videoController.addEventListener('durationchange', (duration) => {
    console.log('Video duration:', duration)
})

// Volume changes
videoController.addEventListener('volumechange', ({ muted, volume }: { muted: boolean, volume: number }) => {
    console.log('Volume changed:', { muted, volume })
})

// Buffering state
videoController.addEventListener('bufferingchange', (isBuffering) => {
    console.log('Buffering:', isBuffering)
})

// Errors
videoController.addEventListener('error', (error) => {
    console.error('Playback error:', error)
})

WebPlayerProps

The WebPlayerProps interface defines the properties that can be passed to the Tivio WebPlayer component. Here's a comprehensive overview of available properties:

Required Properties

  • id (string): Unique identifier for the player instance. This is required and must be unique across your application.

Source Properties

  • source (optional): The video or channel source to play. Can be:
    • VideoPath (string): Path to a video (e.g., "videos/VIDEO_ID")
    • ChannelPath (string): Path to a TV channel (e.g., "tvChannels/CHANNEL_ID")
    • SourceParams (object): Complex source parameters
    • null: No source (player will be idle)

Callback Properties

  • onEnded (optional): Callback function called when video playback ends
  • onProgress (optional): Callback function for video progress events

Layout Properties

  • className (optional): CSS class name for the player container
  • isSameSizeAsParent (optional): If true, the player will inherit the width and height of its parent element

Playback Control Properties

  • autoplay (optional, default: false): Whether to start playback automatically. Note that autoplay may be blocked by browsers before user interaction
  • canReplay (optional, default: true): Whether to show replay functionality
  • doNotSaveWatchPosition (optional): If true, the player won't save the watch position
  • disablePlayNext (optional): If true, the player won't automatically play the next video when current video ends

Visual Properties

  • showMarkers (optional, default: false): Whether to show video markers
  • markerColor (optional): Color for video markers (CSS color value)
  • showTvStreamType (optional): Whether to show TV stream type indicator
  • showCookiesSettings (optional): Whether to show cookies settings
  • showOsd (optional, default: true): Whether to show the On-Screen Display (OSD)
  • showBufferingSpinner (optional, default: true): Whether to show buffering spinner

Audio Properties

  • isMutedByDefault (optional): If true, the player starts muted but can be unmuted

Keyboard Shortcuts Properties

  • enableKeyboardShortcuts (optional, default: true): Whether to enable keyboard shortcuts
  • customShortcuts (optional): Custom keyboard shortcuts configuration:
  {
    toggleFullscreen: number[],    // Array of key codes
    togglePause: number[],
    toggleMute: number[],
    jumpForward: number[],
    jumpBack: number[],
    volumeUp: number[],
    volumeDown: number[]
  }

Ad Block Properties

  • checkAdBlock (optional, default: false): Whether to check for ad blockers and show warnings

Data hooks

Gets information about current user.

useUser: () => {
    user: User | null
    error: string | null
    isInitialized: boolean
}

useRowsInScreen hook

Gets array of Rows objects of specific screen and subscribes to its changes.

/**
 * Use rows in screen
 * @param screenId - screen id (from studio.tiv.io)
 * @param options - subscription options
 */
useRowsInScreen: (screenId: string, options?: PaginationOptions) => {
    pagination: PaginationInterface<Row> | null
    error: Error | null
}

useItemsInRow hook

Gets array of row items objects of specific row and subscribes to its changes.

/**
 * Use row items
 * @param rowId - row ID
 * @param options - subscription options
 */
useItemsInRow: (rowId: string, options?: SubscribeToItemsInRowOptions) => {
    pagination: PaginationInterface<ItemInRow> | null
    error: Error | null
}

useVideo hook

Gets Video object and subscribes to its changes.

/**
 * Use video
 * @param videoId - video id
 */
useVideo: (videoId: string) => {
    error: string | null;
    data: Video | null;
}

useTaggedVideos hook

Gets videos with given tag IDs.

/**
 * Use tagged videos
 * @param tagIds - tag ids
 * @param options - subscription options
 * @public
 */
useTaggedVideos: (tagIds: string[], options?: SubscribeToItemsInRowOptions) => {
    pagination: PaginationInterface<Video> | null
    error: Error | null
}