` element with a unique ID (e.g., id="app"). This element will serve as the container for the live chat widget. The chat widget will be displayed within this `
` element on your webpage.
```html
```
### Initializing Chat SDK
After the script tag including the JavaScript file, add another `
```
**Fields**
| Field | Description |
|----------|-------------------------------------------------|
| username | The username of the user interacting with chat |
| roomId | The ID of the chat room |
| title | The title displayed for the chat interface |
:::important
**Note:** You can obtain the `roomId` after creating a live stream. It will be available on the live stream detail page or through the API.
:::
#### Sample HTML Code
Below is a sample HTML code demonstrating how to integrate the live chat widget into your webpage:
```html
```
---
## Create a live stream instantly
To Create a live stream instantly you need to send an HTTP POST request to the API Endpoint, with the [authentication Header](../authentication.md).
```bash
https://app.tpstreams.com/api/v1//assets/live_streams/
```
**Fields**
| Name | Type | Description | Required |
| ----------- | ----------- | ----------- | ---------- |
| title | string | Specify a text string or identifier which can be used for filtering or searching the live stream.| Yes |
| enable_drm_for_recording | boolean | A boolean value to enable or disable DRM for live stream recording.| No |
| latency | string | Selects the desired latency for the live stream (Options: Low Latency, Normal Latency).| No |
**Sample request body**
```json
{
"title": "Data science Live class",
"enable_drm_for_recording": true,
"latency": "Low Latency"
}
```
For valid requests the API server returns a JSON:
```json
{
"title": "Data science Live class",
"bytes": null,
"type": "livestream",
"video": null,
"id": "5h6EpZQC6sh",
"live_stream": {
"rtmp_url": "",
"stream_key": null,
"status": "Not Started",
"hls_url": "https://d28qihy7z761lk.cloudfront.net/live/gcdem4/5h6EpZQC6sh/video.m3u8",
"start": "2024-04-23 16:56:48",
"transcode_recorded_video": true,
"enable_drm_for_recording": true,
"chat_embed_url": null,
"resolutions": [
"240p",
"480p",
"720p"
],
"enable_drm": true,
"enable_llhls": false,
"latency": "Low Latency"
},
"parent": null,
"parent_id": null
}
```
:::important
- The live stream server typically takes around 20-30 seconds to start.
- Once it's up and running, both the "rtmp_url" and "stream_key" will become accessible.
:::
To automatically receive rtmp_url and stream key as soon as the server is established,[WebHook Response](webhook-response).
After successfully registering the webhook, you will receive an rtmp_url and stream_key as a response paste it in the obs stream settings **Settings > Stream**

---
## Delete Live Stream API
To delete an individual live stream in the organization, send an HTTP DELETE request to the API Endpoint , with the [authentication Header](../authentication.md). This API supports the deletion of both live and scheduled live streams.
```bash
https://app.tpstreams.com/api/v1//assets//
```
For a successful request, status 204 is returned.
:::important
If the streaming live stream or a live stream with an active server is deleted, it will result in stopping the server.
:::
---
## List Live Streams API
To retrieve a list of live streams in the organization, send an HTTP GET request to the API Endpoint, with the [authentication Header](../authentication.md). This API supports to list live streams, scheduled live streams
```http
https://app.tpstreams.com/api/v1//assets/live_streams/
```
**Sample webhook response is as follows**
```json
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"title": "Data Science Live Class",
"bytes": null,
"type": "livestream",
"video": null,
"id": "8DH94uXQgrA",
"live_stream": {
"rtmp_url": "rtmp://52.66.213.19/live",
"stream_key": "org-89b2cy-live-8DH94uXQgrA-7XkI",
"status": "Completed",
"hls_url": "https://d3cydmgt9q030i.cloudfront.net/live/89b2cy/8DH94uXQgrA/video.m3u8",
"start": "2023-12-07 13:02:16",
"transcode_recorded_video": true,
"enable_drm_for_recording": true,
"chat_embed_url": null,
"resolutions": [
"240p",
"480p",
"720p"
]
},
"parent": null,
"parent_id": null
},
{
"title": "GO LIVE NOW",
"bytes": null,
"type": "livestream",
"video": null,
"id": "9QgXR3pPj49",
"live_stream": {
"rtmp_url": "",
"stream_key": null,
"status": "Completed",
"hls_url": "https://d3cydmgt9q030i.cloudfront.net/live/89b2cy/9QgXR3pPj49/video.m3u8",
"start": "2023-12-07 12:34:42",
"transcode_recorded_video": true,
"enable_drm_for_recording": true,
"chat_embed_url": null,
"resolutions": [
"240p",
"480p",
"720p"
]
},
"parent": null,
"parent_id": null
}
]
}
```
---
## Schedule a live stream
#### Schedule a live stream
To Schedule a live stream you need to send an HTTP POST request to the API Endpoint, with the [authentication Header](/server-api/authentication.md).
```bash
https://app.tpstreams.com/api/v1//assets/live_streams/
```
**Fields**
| Name | Type | Description | Required |
| ----------- | ----------- | ----------- | ---------- |
| title | string | Specify a text string or identifier which can be used for filtering or searching the live stream.| Yes |
| start | string | Specify the date and time (in the format: "YYYY-MM-DD HH:MM:SS") when the live stream should be scheduled.| Yes |
| enable_drm_for_recording | boolean | A boolean value to enable or disable DRM for live stream recording.| No |
**Sample request body**
```json
{
"title": "Data science Live class",
"start": "2024-10-05 15:30:00",
"enable_drm_for_recording": false
}
```
For valid requests the API server returns a JSON:
```json
{
"title": "Data science Live class",
"bytes": null,
"type": "livestream",
"video": null,
"id": "AuC9yX2EtBr",
"live_stream": {
"rtmp_url": "",
"stream_key": null,
"status": "Not Started",
"hls_url": "https://d28qihy7z761lk.cloudfront.net/live/gnarys/AuC9yX2EtBr/video.m3u8",
"start": "2024-10-05 15:30:00",
"transcode_recorded_video": true,
"enable_drm_for_recording": false,
"chat_embed_url": null,
"resolutions": [
"240p",
"480p",
"720p"
]
},
"parent": null,
"parent_id": null
}
```
Live stream is created with the scheduled time for you to start the server later.
Above response has the details of the live stream scheduled which can also be obtained by API
**`/api/v1//assets//`**
:::important
- Scheduled live streams will not automatically start at the specified time; they must be manually initiated.
- RTMP URL and stream key will be available once you start the stream
:::
---
## Start the scheduled live stream
To Start a server for the scheduled live stream you need to send an HTTP POST request to the API Endpoint, with the [authentication Header](/server-api/authentication.md).
```bash
https://app.tpstreams.com/api/v1//assets//start_server/
```
For valid requests the API server returns a JSON:
```json
{
"title": "Data science Live class",
"bytes": null,
"type": "livestream",
"video": null,
"id": "8XGEEj6ptnB",
"live_stream": {
"rtmp_url": "",
"stream_key": null,
"status": "Not Started",
"hls_url": "https://d3cydmgt9q030i.cloudfront.net/live/edee9b/8XGEEj6ptnB/video.m3u8",
"start": "2024-10-05 15:30:00",
"transcode_recorded_video": true,
"enable_drm_for_recording": false,
"chat_embed_url": "https://app.tpstreams.com/live-chat/edee9b/8XGEEj6ptnB/",
"resolutions": [
"240p",
"480p",
"720p"
]
},
"parent": null,
"parent_id": null
}
```
This will start the server for specified live stream
Above response has the details of the live stream started which can also be obtained by API
**`/api/v1//assets//`**
:::important
- The live stream server typically takes around 20-30 seconds to start.
- Once it's up and running, both the "rtmp_url" and "stream_key" will become accessible.
:::
To automatically receive rtmp_url and stream key as soon as the server is established,[Create a Web Hook](/server-api/webhooks.md).
After successfully registering the webhook, you will receive an updated webhook response.
---
## Live Stream Events & Status
This document explains the workflow and status of your live stream, describing the progression of events from scheduling to final video availability. This guide will help you understand the real-time status updates and webhook events you receive.
***
### Live Stream Statuses
Live stream statuses reflect the overall state of your live stream. These are visible on the asset details page.
| **Status** | **Meaning** |
| :--- | :--- |
| **`NOT_STARTED`** | The live stream has been scheduled, and the server is being provisioned. Your stream is not yet ready to accept a broadcast. |
| **`STREAMING`** | The server is ready and actively receiving a live video feed. This means your broadcast is live and viewable by your audience. |
| **`RECORDING`** | The stream is actively being recorded. The content is being captured and stored for later use, even if the live broadcast itself ends. |
| **`DISCONNECTED`** | The connection between your broadcasting software (OBS) and the streaming server has been lost. This could be due to a network issue, a power failure, or a temporary disconnection. The server remains active for a short period, allowing you to reconnect and resume the stream without creating a new one. |
| **`STOPPED`** | The live stream has been manually ended by the user or has timed out after a disconnection. The server has been terminated, and the live stream is no longer active. To stream again, you must create a new live stream. |
| **`COMPLETED`** | The live stream has finished, and the recorded video is now fully processed and available. This status is typically seen after a stream has ended and the recording has been transcoded and stored. |
| **`ERROR`** | An unexpected error has occurred, preventing the live stream from continuing. |
***
### Live Stream Events
Events are the specific, immediate actions (like starting or stopping) that change the stream's Status. These messages appear in your detailed logs and are sent directly to your system via webhooks.
| **Event Type** | **Meaning** |
| :--- | :--- |
| **`CREATED`** | The initial event, triggered when a live stream is created. This event signals that a server instance is being spun up to handle the broadcast. This corresponds to the **`NOT_STARTED`** status. |
| **`ON_PUBLISH`** | The event that indicates a successful connection from the broadcasting software (OBS). The stream is now live. This corresponds to the **`STREAMING`** status. |
| **`ON_PUBLISH_DONE`** | The event that indicates a temporary disconnection from the broadcasting software. This could be due to a user stopping the stream, a network interruption, or another issue. The server is still running for a short time, giving you a chance to reconnect. This corresponds to the **`DISCONNECTED`** status. |
| **`STOPPED`** | The final event, triggered when a live stream is permanently ended. The server has been terminated, and the stream cannot be resumed. This corresponds to the **`STOPPED`** status. |
| **`RECORDING`** | An event indicating that the stream is being recorded. This is separate from the `ON_PUBLISH` event and focuses on the recording process. This corresponds to the **`RECORDING`** status. |
| **`COMPLETED`** | An event signaling that the live stream recording has been fully processed, and the final video file is ready. This corresponds to the **`COMPLETED`** status. |
| **`ERROR`** | An event indicating that an error has occurred during the live stream. This corresponds to the **`ERROR`** status. |
***
### Example Live Stream Workflow
Sample API response
```json
{
"id": "9JGyz9njKnj",
"title": "Test session",
"type": "livestream",
"live_stream": {
...
"activities": [
{
"status": "On Publish",
"timestamp": "September 22, 2025, 05:59 PM"
},
{
"status": "On Publish Done",
"timestamp": "September 22, 2025, 06:06 PM"
},
{
"status": "Stopped",
"timestamp": "September 22, 2025, 06:17 PM"
}
]
}
}
```
1. A live stream is scheduled.
- **Action:** The server is initiated, and an RTMP key and ID are generated.
- **Status:** `NOT_STARTED`
- **Event:** `CREATED`
2. The broadcaster starts streaming from their broadcasting software (e.g., OBS).
- **Action:** The live stream is now active and viewable.
- **Status:** `STREAMING`
- **Event:** `ON_PUBLISH`
3. The broadcaster's network temporarily disconnects.
- **Action:** The stream is temporarily paused, but the server is still running, awaiting a reconnection.
- **Status:** `DISCONNECTED`
- **Event:** `ON_PUBLISH_DONE`
4. The broadcaster fails to reconnect, or the stream is manually stopped.
- **Action:** The server is terminated, and the live stream has officially ended.
- **Status:** `STOPPED`
- **Event:** `STOPPED`
---
## Stop a live stream
To Stop a live stream you need to send an HTTP POST request to the API Endpoint, with the [authentication Header](../authentication.md).
```bash
https://app.tpstreams.com/api/v1//assets//stop_live_stream/
```
This will stop the specified live stream
For valid requests the API server returns a JSON:
```json
{
"message": "Live stream stopped successfully",
"trim_scheduled": false
}
```
### Schedule Trim After Live Stream
You can optionally add trim parameters to the stop API request body to schedule a trim operation that will be executed after the live stream recording is completed and transcoded. This allows you to trim the recorded video directly when stopping the live stream.
**Fields for Trim Scheduling**
| Name | Type | Description | Required |
| ----------- | ----------- | ----------- | ---------- |
| start_time | integer | Start time for trim. Can be either relative seconds from the beginning of the recording or epoch timestamp | Yes (for trim) |
| end_time | integer | End time for trim. Can be either relative seconds from the beginning of the recording or epoch timestamp | Yes (for trim) |
**Timestamp Formats**
You can specify trim times using either of the following formats:
1. **Relative Seconds**: Time in seconds from the beginning of the recording
2. **Epoch Timestamps**: Unix timestamps
:::important
- Both `start_time` and `end_time` must use the same format (either relative seconds or epoch timestamps).
- Mixing formats in a single request is not allowed
:::
**Sample request body with relative seconds**
```json
{
"start_time": 30,
"end_time": 120
}
```
**Sample request body with epoch timestamps**
```json
{
"start_time": 1736754600,
"end_time": 1736754900
}
```
:::important
When scheduling a trim:
- Both `start_time` and `end_time` are required
- Both must be non-negative integers
- `start_time` must be less than `end_time`
- `end_time` cannot exceed the total duration of the live stream recording
- The trim operation will be executed automatically after transcoding completes
:::
**Response with trim scheduled**
```json
{
"message": "Live stream stopped successfully",
"trim_scheduled": true
}
```
:::important
If the value of the parameter "transcode_recorded_video" is set to true, you will receive the video object in the webhook response.
:::
**Sample webhook response is as follows**
```json
{
"title": "Data science Live class",
"bytes": null,
"type": "livestream",
"video": {
"progress": 0,
"thumbnails": [],
"status": "Completed",
"playback_url": "https://d7pdowhru2wq4.cloudfront.net/transcoded/9328558d-e0a5-4093-b3b9-8f15ad1550d8/video.m3u8",
"dash_url": "https://d7pdowhru2wq4.cloudfront.net/transcoded/9328558d-e0a5-4093-b3b9-8f15ad1550d8/video.mpd",
"preview_thumbnail_url": null,
"format": "abr",
"resolutions": ["240p", "360p", "480p", "720p"],
"video_codec": "h264",
"audio_codec": "aac",
"enable_drm_for_recording": true,
"tracks": [],
"inputs": [
{
"url": "https://static.testpress.in/Data_science_Live_class.mp4"
}
],
},
"id": "4PtERT9d9uK",
"live_stream": {
"rtmp_url": "rtmp://23.427.127.24/live",
"stream_key": "org-4xu8ay-live-4PtERT9d9uK-jKP4",
"status": "Completed",
"hls_url": "https://d28qihy7z761lk.cloudfront.net/live/4xu8ay/4PtERT9d9uK/video.m3u8",
"start": null,
"transcode_recorded_video": true,
"chat_embed_url":"https://app.tpstreams.com/live-chat/4PtERT9d9uK/"
},
"parent_id": null
}
```
---
## WebHook
To notify your application about things that happen asynchronously ,
[Create a Web Hook](../webhooks.md).
After successfully registering the webhook, whenever the status of live stream changes, response will be sent to the webhook.
**Sample webhook response for livestream is as follows**
```json
{
"title": "Data science Live class",
"bytes": null,
"type": "livestream",
"video": null,
"id": "4PtERT9d9uK",
"live_stream": {
"rtmp_url": "rtmp://23.427.127.24/live",
"stream_key": "org-4xu8ay-live-4PtERT9d9uK-jKP4",
"status": "Streaming",
"hls_url": "https://d28qihy7z761lk.cloudfront.net/live/4xu8ay/4PtERT9d9uK/video.m3u8",
"start": null,
"transcode_recorded_video": true,
"chat_embed_url":"https://app.tpstreams.com/live-chat/4PtERT9d9uK/"
},
"parent_id": null
}
```
##### Streaming Status Table
| Status | Description |
|--------------|-------------|
| Not Started | Live stream server not created or started. |
| Streaming | The live stream is active, typically streamed via OBS. |
| Recording | After stopping the live stream, it enters recording state, initiating transcoding. |
| Disconnected | Live streaming is stopped in OBS, either manually or due to network issues. |
| Error | Error encountered while starting the live stream. |
| Completed | Transcoding is completed, transitioning the status to "Completed." |
:::important
When the status of TPStreams changes:
- If the status of the live stream changes to **Disconnected**, initiate a direct reconnection using OBS.
- If the status changes to **Error**, create a new live stream.
---
## Organization
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
### Get all the organizations that belong to the user
you need to send an HTTP GET request to the API Endpoint, with the [authentication Header](../server-api/authentication.md), to list all the organizations belonging to the user.
```bash
https://app.tpstreams.com/api/v1/organizations/
```
**cURL**
```bash
curl --request GET \
--url https://app.tpstreams.com/api/v1/organizations/ \
--header 'authorization: token 378ad87dc80534b75dcb674da1e7a0145b27461c936c4091c17546fdca6baac1' \
--header 'cache-control: no-cache'
```
**Ruby**
``` rb
require 'uri'
require 'net/http'
url = URI("https://app.tpstreams.com/api/v1/organizations/")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Get.new(url)
request["authorization"] = 'token 378ad87dc80534b75dcb674da1e7a0145b27461c936c4091c17546fdca6baac1'
request["cache-control"] = 'no-cache'
response = http.request(request)
puts response.read_body
```
**Python**
```py
import requests
url = "https://app.tpstreams.com/api/v1/organizations/"
headers = {
'authorization': "token 378ad87dc80534b75dcb674da1e7a0145b27461c936c4091c17546fdca6baac1",
'cache-control': "no-cache"
}
response = requests.request("GET", url, headers=headers)
print(response.text)
```
**C#**
```c
var client = new RestClient("https://app.tpstreams.com/api/v1/organizations/");
var request = new RestRequest(Method.GET);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("authorization", "token 378ad87dc80534b75dcb674da1e7a0145b27461c936c4091c17546fdca6baac1");
IRestResponse response = client.Execute(request);
```
#### Response
```json
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"name": "Testpress",
"uuid": "6dnpyx",
"drm_aes_signing_iv": "52B545d56c6G8da12dee470fb656413c",
"drm_aes_signing_key": "4569d18e02b9e8ebf5ef145693454e729e0b95e6bb8a345db88cfe15d7904785"
},
{
"name": "Streams",
"uuid": "7coqzy",
"drm_aes_signing_iv": "90a541d5cc608da12dee470fb654413c",
"drm_aes_signing_key": "1239d18e02b9e8ebf5ef131a9f564e729e0b95e6bb8a78ddb88cfe15d7904723"
}
]
}
```
---
## Subtitles
Manage subtitles for your video assets. You can generate English subtitles automatically, upload your own subtitle files in WebVTT (.vtt) format, list existing subtitles, and delete them.
### Generate Subtitle for an Asset
To generate subtitles for a video asset, you need to send an HTTP POST request to the API Endpoint, with the [authentication Header](../server-api/authentication.md).
**Endpoint**
```bash
https://app.tpstreams.com/api/v1//assets//generate_subtitle/
```
**Description**
This endpoint triggers automatic subtitle generation for a video asset using speech-to-text technology. The system will generate English subtitles automatically and save them as a WebVTT (.vtt) file.
**Request Body**
No request body is required for this endpoint.
**Response**
For valid requests the API server returns the complete asset data in JSON format with status code 201:
```json
{
"title": "Big Buck Bunny Video",
"bytes": 26990804,
"type": "video",
"video": {
"progress": 0,
"thumbnails": [
],
"status": "Completed",
"playback_url": "https://d28qihy7z761lk.cloudfront.net/transcoded/7cFHfFSfjna/video.m3u8",
"dash_url": "https://d28qihy7z761lk.cloudfront.net/transcoded/7cFHfFSfjna/video.mpd",
"preview_thumbnail_url": "https://d28qihy7z761lk.cloudfront.net/transcoded/7cFHfFSfjna/thumbnails/thumbnail_4.png",
"cover_thumbnail_url": "https://d28qihy7z761lk.cloudfront.net/transcoded/7cFHfFSfjna/thumbnails/thumbnail_4.png",
"format": "abr",
"resolutions": [
"240p",
"360p",
"480p",
"720p"
],
"video_codec": "h264",
"audio_codec": "aac",
"enable_drm": true,
"tracks": [],
"inputs": [
{
"url": "private/677155207a6847b5b5a8d70cfaf4a8a1.mp4"
}
],
"transmux_only": null,
"duration": 19,
"content_protection_type": "drm"
},
"id": "7cFHfFSfjna",
"live_stream": null,
"parent_id": "BmN3MXSq5z6"
}
```
**Pricing**
Auto-generated English subtitles cost $0.071 per minute of video content.
:::important
- Subtitle generation is an asynchronous process that may take several minutes
- Only one auto-generated subtitle track per video is allowed
- Email notifications are sent upon completion or failure
:::
### Upload Subtitles to an Asset
To upload subtitles to an asset, you need to send an HTTP POST request to the API Endpoint, with the [authentication Header](../server-api/authentication.md).
**Endpoint**
```bash
https://app.tpstreams.com/api/v1//assets//upload_subtitle/
```
**Fields**
| Name | Type | Description | Required |
| ----------- | ----------- | ----------- | -------- |
| subtitle | .vtt file | File Containing Subtitles | Yes |
| name | string | Name of the subtitles | No |
| language | string | Language code of the subtitles | No |
Send the subtitle file using form-data in the request body.
To know language code please check [Language-codes](../language-codes)
**Sample Postman request body (Form-data only)**

**Response**
For valid requests the API server returns a JSON:
```json
{
"detail": "Subtitle uploaded successfully"
}
```
:::important
For subtitle upload, use form-data in the request body. Select your .vtt file with the "subtitle" key.
:::
### List Subtitles for an Asset
To retrieve a list of all subtitles associated with a video asset, you need to send an HTTP GET request to the API Endpoint, with the [authentication Header](../server-api/authentication.md).
**Endpoint**
```bash
https://app.tpstreams.com/api/v1//assets//subtitles/
```
**Response**
For valid requests the API server returns a JSON list of subtitle tracks:
```json
[
{
"id": 12,
"type": "Subtitle",
"name": "English",
"url": "https://d28qihy7z761lk.cloudfront.net/transcoded/7cFHfFSfjna/subtitles/en.vtt",
"bytes": 1024,
"subtitle_type": "Auto Generated"
},
{
"id": 15,
"type": "Subtitle",
"name": "Spanish",
"url": "https://d28qihy7z761lk.cloudfront.net/transcoded/7cFHfFSfjna/subtitles/es.vtt",
"bytes": 2048,
"subtitle_type": "Uploaded"
}
]
```
### Delete a Subtitle
To delete a specific subtitle track, send an HTTP DELETE request to the API Endpoint, with the [authentication Header](../server-api/authentication.md).
**Endpoint**
```bash
https://app.tpstreams.com/api/v1//subtitles//
```
Alternatively, you can delete a subtitle using the asset context:
```bash
https://app.tpstreams.com/api/v1//assets//subtitles//
```
**Response**
For successful deletion, the API server returns a 204 No Content status code with an empty body.
:::important
You cannot directly delete an auto-generated subtitle track. Attempting to do so will return a 403 Forbidden response.
:::
---
## Usages
The Usage API helps you get your organization usage's.
### Get Your Organization's Usage
To retrieve your organization's data usage, send an HTTP GET request to the API Endpoint, with the [authentication Header](../server-api/authentication.md).
**Endpoint**
```bash
https://app.tpstreams.com/api/v1//assets_usage/
```
**Query Parameter **
The Usage API provides various options to filter data using query parameters:
| Parameter | Description | Type |
|-------------|-------------------------|-----------------|
| month | Filter by month | Integer |
| year | Filter by year | Integer |
| day | Filter by day | Integer |
| time_frame | Filter by timeframe | String ("daily" or "monthly") |
| start | Start date | Date (YYYY-MM-DD) |
| end | End date | Date (YYYY-MM-DD) |
| ordering | Order results | "date" (asc) or "-date" (desc) |
**Response**
```json
{
"count": 3,
"next": null,
"previous": null,
"results": [
{
"id": 5385,
"date": "2023-04-01",
"bandwidth_used": 245540965,
"subtitle_generation_cost": 0,
"live_stream_usage": 0,
"active_storage_bytes": 1198712474,
"deleted_storage_bytes": 41007799,
"total_storage_bytes": 1239720273
},
{
"id": 5253,
"date": "2023-05-01",
"bandwidth_used": 2484764822,
"subtitle_generation_cost": 0,
"live_stream_usage": 0,
"active_storage_bytes": 10651243573,
"deleted_storage_bytes": 1043743829,
"total_storage_bytes": 11694987402
},
{
"id": 5178,
"date": "2023-06-01",
"bandwidth_used": 27907667,
"subtitle_generation_cost": 0,
"live_stream_usage": 0,
"active_storage_bytes": 10891400407,
"deleted_storage_bytes": 1043973205,
"total_storage_bytes": 11935373612
}
]
}
```
---
## Web Hooks
Streams uses webhooks to notify your application about things that happen asynchronously, apart from the API request-response cycle. For example, you may want to update something on your end when a video asset status changes from queued to ready or errored. When these events happen, Streams will make a POST request to the address you give us and you can do whatever you need with it on your end.
### Configure Webhook
Webhooks can be configured using a webhook endpoint URL and a secret token to ensure authenticity. Once the webhook is configured for a video source or different video sources, a notification will be sent for each event for the sources. Here, the parameter Secret Token is used to ensure in your code that the Webhook is coming from Streams.
### Create a Webhook
To Create a webhook you need to send an HTTP POST request to the API Endpoint, with the [Authentication Header](../server-api/authentication.md).
```bash
https://app.tpstreams.com/api/v1//webhooks/
```
**Fields**
| Name | Type | Description | Required |
| ----------- | ----------- | ----------- | ---------- |
| url | string | URL to which asset data to be sent. | True |
| secret_token | string | secret_token will be sent in x-streams-token.| True |
**Sample request body**
```json
{
"url": "https://sample.beeceptor.com",
"secret_token": "abcdef",
}
```
For valid requests the API server returns a JSON:
```json
{
"url": "https://sample.beeceptor.com",
"secret_token": "abcdef",
"id": "c4cf9c5f-0b60-4e5c-9db9-81321ffe31d5"
}
```
### List all webhooks
To get all the webhooks, you need to send an HTTP GET request to the API Endpoint, with the [Authentication Header](../server-api/authentication.md).
**Endpoint**
```bash
https://app.tpstreams.com/api/v1//webhooks/
```
**Response**
```json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"url": "https://sample.beeceptor.com",
"secret_token": "abcdef",
"id": "c4cf9c5f-0b60-4e5c-9db9-81321ffe31d5"
}
]
}
```
### Update a webhook
To update a webhook, you need to send an HTTP PUT request to the API Endpoint, with the [Authentication Header](../server-api/authentication.md).
**Endpoint**
```bash
https://app.tpstreams.com/api/v1//webhooks//
```
**Response**
```json
{
"url": "https://sample1.beeceptor.com",
"secret_token": "abcdef",
"id": "c4cf9c5f-0b60-4e5c-9db9-81321ffe31d5"
}
```
### Delete a webhook
To delete a webhook, you need to send an HTTP DELETE request to the API Endpoint, with the [Authentication Header](../server-api/authentication.md).
**Endpoint**
```bash
https://app.tpstreams.com/api/v1//webhooks//
```
### WebHook Response
Whenever the status of asset changes, response will be sent to the webhook.
**Sample webhook response is as follows**
```json
{
"id": "9328558d-e0a5-4093-b3b9-8f15ad1550d8", // asset id
"title": "Big Buck Bunny Video",
"bytes": null,
"type": "video",
"video": {
"progress": 0,
"thumbnails": [],
"status": "Completed",
"playback_url": "https://d7pdowhru2wq4.cloudfront.net/transcoded/9328558d-e0a5-4093-b3b9-8f15ad1550d8/video.m3u8",
"dash_url": "https://d7pdowhru2wq4.cloudfront.net/transcoded/9328558d-e0a5-4093-b3b9-8f15ad1550d8/video.mpd",
"preview_thumbnail_url": null,
"format": "abr",
"resolutions": ["240p", "360p", "480p", "720p"],
"video_codec": "h264",
"audio_codec": "aac",
"enable_drm": true,
"tracks": [],
"inputs": [
{
"url": "https://static.testpress.in/BigBuckBunny.mp4"
}
],
},
}
```
---
## Subtitle Service
The Subtitle Service API allows you to asynchronously generate subtitles for any valid audio or video URL, independently of an asset. The system will process the media and generate subtitles in WebVTT (.vtt) format.
### Create a Subtitle Job
To trigger subtitle generation for a media URL, send an HTTP POST request to the API Endpoint with the [authentication Header](../server-api/authentication.md).
**Endpoint**
```bash
https://app.tpstreams.com/api/v1//subtitle_jobs/
```
**Fields**
| Name | Type | Description | Required |
| --------------- | ------ | ------------------------------------------------------------------------------------------------------- | -------- |
| url | string | A publicly accessible URL to the audio or video file. | Yes |
| client_metadata | object | A JSON object (up to 10KB) to store any custom metadata you want to associate with this job. | No |
**Sample Request Body (JSON)**
```json
{
"url": "https://example.com/audio.mp3",
"client_metadata": {
"my_internal_id": "12345"
}
}
```
**Response**
For valid requests, the API server returns a JSON response with status code `201 Created`:
```json
{
"id": "7cFHfFSfjna",
"url": "https://example.com/audio.mp3",
"language": "en",
"status": "Queued",
"subtitle_url": null,
"audio_duration": null,
"error_code": null,
"error_message": null,
"client_metadata": {
"my_internal_id": "12345"
},
"created": "2023-10-27T10:00:00.000000Z"
}
```
:::info
Subtitle generation is an asynchronous process. The initial status will be `Queued` or `Processing`. You can poll the Retrieve Job API to check when it reaches `Completed`.
:::
### Retrieve a Subtitle Job
To get the current status and details of a specific subtitle job, send an HTTP GET request to the API Endpoint.
**Endpoint**
```bash
https://app.tpstreams.com/api/v1//subtitle_jobs//
```
**Response**
For valid requests, the API server returns a JSON response with status code `200 OK`:
```json
{
"id": "7cFHfFSfjna",
"url": "https://example.com/audio.mp3",
"language": "en",
"status": "Completed",
"subtitle_url": "https://d28qihy7z761lk.cloudfront.net/private/subtitle_jobs/7cFHfFSfjna.vtt",
"audio_duration": "00:02:00.000000",
"error_code": null,
"error_message": null,
"client_metadata": {
"my_internal_id": "12345"
},
"created": "2023-10-27T10:00:00.000000Z"
}
```
### List Subtitle Jobs
To retrieve a list of all your subtitle jobs, send an HTTP GET request to the API Endpoint.
**Endpoint**
```bash
https://app.tpstreams.com/api/v1//subtitle_jobs/
```
**Response**
For valid requests, the API server returns a JSON array of subtitle jobs (can be paginated) with status code `200 OK`:
```json
[
{
"id": "7cFHfFSfjna",
"url": "https://example.com/audio.mp3",
"language": "en",
"status": "Completed",
"subtitle_url": "https://d28qihy7z761lk.cloudfront.net/private/subtitle_jobs/7cFHfFSfjna.vtt",
"audio_duration": "00:02:00.000000",
"error_code": null,
"error_message": null,
"client_metadata": null,
"created": "2023-10-27T10:00:00.000000Z"
}
]
```
---
## Getting Started with TPStorage
TPStorage is an **S3-compatible object storage** service. You can access and manage your storage using your **Access Key** and **Secret Key**, similar to Amazon S3.
### Installation
Make sure `boto3` is installed:
```bash
pip install boto3
```
### Configuration
Use the following sample code to connect to TPStorage:
```python
import boto3
# Initialize the TPStorage client
tpstorage = boto3.client(
's3',
endpoint_url='https://storage1.tpstreams.com', # TPStorage endpoint
aws_access_key_id='YOUR_ACCESS_KEY', # Replace with your Access Key
aws_secret_access_key='YOUR_SECRET_KEY', # Replace with your Secret Key
region_name='us-east-1', # Required by boto3, ignored by TPStorage
config=boto3.session.Config(signature_version='s3v4')
)
```
### Basic Operations
#### Listing Buckets
```python
# List all buckets
response = tpstorage.list_buckets()
for bucket in response.get('Buckets', []):
print(bucket['Name'])
```
#### Uploading a File
```python
tpstorage.upload_file('local_file.txt', 'my-bucket', 'remote_file.txt')
```
#### Generating a Temporary Download URL
```python
url = tpstorage.generate_presigned_url(
'get_object',
Params={'Bucket': 'my-bucket', 'Key': 'remote_file.txt'},
ExpiresIn=3600 # Link valid for 1 hour
)
print("Download URL:", url)
```
### Access Details
| Setting | Value |
|----|----|
| **Endpoint URL** | `https://storage1.tpstreams.com` |
| **Access Key** | `` |
| **Secret Key** | `` |
| **Compatibility** | S3 API (boto3, AWS SDKs, rclone, etc.) |
| **Region** | `us-east-1` (placeholder, not used by TPStorage) |
---
## Introduction(Tpstorage)
TPStorage is an **S3-compatible object storage** service that allows you to store and manage your files using familiar S3 API patterns. This documentation will help you get started with TPStorage and integrate it into your applications.
### What is TPStorage?
TPStorage provides:
- **S3-compatible API** - Use existing S3 tools and libraries
- **Python boto3 support** - Seamless integration with Python applications
- **Secure access** - Access Key and Secret Key authentication
- **Presigned URLs** - Generate temporary download/upload links
- **Bucket management** - Organize your files in buckets
### Quick Start
1. **Install boto3**: `pip install boto3`
2. **Configure your client** with TPStorage endpoint
3. **Start uploading and managing files**
### Getting Started
Follow the **[Getting Started](getting-started)** guide to set up TPStorage with Python and boto3.
### Getting Help
If you need assistance with TPStorage integration, please refer to the detailed examples in our documentation or contact our support team.
---
## Get Transcoding Job detail
To retrieve the details of a transcoding job, make a GET request to the following API endpoint , with the [authentication Header](../server-api/authentication.md).
```base
https://app.tpstreams.com/api/v1//transcoding_jobs//
```
Replace `` with your organization's unique identifier and `` with the specific job ID you want to retrieve details for.
##### Sample Response
```json
{
"id": "5KQfnXCg8Qh",
"resolutions": [
"240p",
"480p"
],
"video_duration": 120,
"status": "Completed",
"input_url": "https://example.com/input-video.mp4",
"output_path": "s3://example-bucket/path/?access_key=E8WPS6H1A4OYD3ZNVMR&secret_key=N1dYpS2cTk5AeH6jWf8TgBh9Ji0MkL1N2O3P",
"start_time": "2023-04-20T23:20:06.034924+12:00",
"end_time": "2023-04-20T23:40:06.034924+12:00",
"error_message": null,
"client_metadata": {
"client_id": "12345",
"project": "marketing_campaign_2024"
}
}
```
:::important
In case of transcoding errors, details will be provided in the "error_message" field of the response.
:::
---
## List all Transcoding Jobs
To list all transcoding jobs, make a GET request to the following API endpoint, with the [authentication Header](../server-api/authentication.md).
```base
https://app.tpstreams.com/api/v1//transcoding_jobs/
```
Replace `` with your organization's unique identifier.
#### Query Parameters
You can filter and order the list of transcoding jobs using the following query parameters:
| Parameter | Type | Description |
| :--- | :--- | :--- |
| `status` | string | Filter jobs by status. You can provide multiple statuses by repeating the parameter. Options: `queued`, `transcoding`, `completed`, `error`. |
| `enable_drm` | boolean | Filter jobs by whether DRM is enabled (`true`) or disabled (`false`). |
| `created_from` | string | Filter jobs created on or after this date and time (ISO 8601 format). |
| `created_to` | string | Filter jobs created on or before this date and time (ISO 8601 format). |
| `ordering` | string | Order the jobs by creation time. Options: `-created` (Latest first), `created` (Oldest first). |
##### Example request with filters
`https://app.tpstreams.com/api/v1//transcoding_jobs/?status=completed&enable_drm=true&created_from=2023-04-20T00:00:00Z&ordering=-created`
#### Sample Response
The response is paginated and contains a list of transcoding jobs.
```json
{
"count": 42,
"next": "https://app.tpstreams.com/api/v1//transcoding_jobs/?page=2",
"previous": null,
"results": [
{
"id": "5KQfnXCg8Qh",
"resolutions": [
"240p",
"480p"
],
"video_duration": 120,
"status": "completed",
"input_url": "https://example.com/input-video.mp4",
"output_path": "s3://example-bucket/path/?access_key=&secret_key=®ion=",
"start_time": "2023-04-20T23:20:06.034924+12:00",
"end_time": "2023-04-20T23:40:06.034924+12:00",
"error_message": null,
"enable_drm": true
},
{
"id": "7XkLp9mN2O",
"resolutions": [
"720p"
],
"video_duration": 300,
"status": "queued",
"input_url": "https://example.com/another-video.mp4",
"output_path": "s3://example-bucket/another-path/?access_key=&secret_key=®ion=",
"start_time": null,
"end_time": null,
"error_message": null,
"enable_drm": false
}
]
}
```
---
## Trancode a video
To transcode a video, you need to make a POST request to the following API endpoint, with the [authentication Header](../server-api/authentication.md).
```bash
https://app.tpstreams.com/api/v1//transcoding_jobs/
```
Replace `` with your organization's unique identifier.
##### Sample payload:
You have the flexibility to choose the method that best fits your workflow. Whether you prefer using an external video URL or a direct S3 bucket path, both options are available to you:
**1. External Video URL:**
```json
{
"input_url": "https://example.com/input-video.mp4",
"output_path": "s3://example-bucket/path/?access_key=&secret_key=®ion=",
"resolutions": ["240p", "480p"],
"enable_drm": true,
"client_metadata": {
"client_id": "12345",
"project": "marketing_campaign_2024"
}
}
```
**2. Direct S3 Bucket Path:**
```json
{
"input_path": "s3://example-bucket/video.mp4/?access_key=&secret_key=®ion=",
"output_path": "s3://example-bucket/path/?access_key=&secret_key=®ion=",
"resolutions": ["240p", "480p"],
"enable_drm": true,
"client_metadata": {
"client_id": "12345",
"project": "marketing_campaign_2024"
}
}
```
Here's a breakdown of the fields in the payload:
| **Name** | **Description** |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------- |
| input_url | The URL of the input video file you want to transcode. **OR** The S3 bucket path of the input video file. If using the S3 path, ensure the access_key , secret_key and region match the bucket's credentials. |
| output_path | The S3 bucket path where the transcoded video files will be stored (e.g., `s3://bucket/path/`). Credentials (`access_key`, `secret_key`, `region`) may be included as query parameters, or omitted if storage is configured for your account. See the note below for more details. |
| resolutions | An array of resolutions for transcoding the video. Specify multiple resolutions as needed. Options include 240p, 360p, 480p, 720p, and 1080p. |
| enable_drm | (Optional) This flag allows you to enable Digital Rights Management (DRM) for your video. When set to `true`, your content will be encrypted using Widevine and Fairplay, and a `drm_content_id` will be returned in the response. This ID is required to generate playback licenses. Defaults to `false`. |
| client_metadata | (Optional) A JSON object containing custom metadata for your reference. This metadata will be returned in the job response and webhook notifications. Maximum size: 10 KB. |
:::important
- **output_path** : Storage credentials (`access_key`, `secret_key`, and `region`) are optional. You may omit them if storage is configured for your account. If storage is not configured, please contact **TPStreams Support** to enable it before omitting these fields.
- **client_metadata** : This field is optional and can be used to store custom data for your internal purposes (e.g., tracking client IDs, project names, or reference information). The server does not modify or interpret this data—it stores and returns it exactly as provided in API responses and webhook notifications.
:::
##### Response
Upon a successful request, you will receive a response like below with information about the transcoding job, including a unique job ID. You can use this job ID to monitor the progress and check the status of your transcoding job.
```json
{
"id": "5KQfnXCg8Qh",
"resolutions": [
"240p",
"480p"
],
"video_duration": null,
"status": "Queued",
"enable_drm": true,
"drm_content_id": "8216dcdb90c34a4f8abf1671630a8817",
"input_url": "https://example.com/input-video.mp4",
"output_path": "s3://example-bucket/path/?access_key=&secret_key=®ion=",
"start_time": null,
"end_time": null,
"error_message": null,
"client_metadata": {
"client_id": "12345",
"project": "marketing_campaign_2024"
}
}
```
:::important
In case of transcoding errors, details will be provided in the "error_message" field of the response.
:::
#### Get notified on status change
We offer webhook integration to keep you informed about the status and progress of your transcoding jobs in real-time. With webhook integration, you can receive notifications as soon as your job status changes, making it easier to track and manage your video transcoding tasks.
To register a webhook for your organization, Please check webhook [documentation](../server-api/webhooks.md).
Upon registering a webhook, you will receive a status change along with information about your transcoding job. Here is an example of the response you might receive:
```json
{
"id": "5KQfnXCg8Qh",
"resolutions": [
"240p",
"480p"
],
"video_duration": null,
"status": "transcoding",
"enable_drm": true,
"drm_content_id": "8216dcdb90c34a4f8abf1671630a8817",
"input_url": "https://example.com/input-video.mp4",
"output_path": "s3://example-bucket/path/?access_key=&secret_key=®ion=",
"start_time": "2023-11-22T12:30:00Z",
"end_time": null,
"error_message": null,
"client_metadata": {
"client_id": "12345",
"project": "marketing_campaign_2024"
}
}
```
:::important
The `drm_content_id` is required to generate playback licenses.
:::
#### Playing DRM Protected Content
If you enabled DRM, the response includes a `drm_content_id`. You will need to use this ID as the `content_id` when generating a signed license token for playback.
Learn how to generate the license and URL in the [Get license (Widevine)](../drm-service/widevine-configuration) or [FairPlay Configuration](../drm-service/fairplay-configuration) guides.
---
## Cancel Transcoding Job
To cancel a transcoding job, you need to send an HTTP DELETE request to the API Endpoint , with the [authentication Header](../server-api/authentication.md).
**Endpoint**
```bash
https://app.tpstreams.com/api/v1//transcoding_jobs//
```
**Response**
Upon a successful cancellation request, the API will respond with a message confirming the cancellation. Here is an example of the response you might receive:
```json
{
"message": "Job cancelled successfully."
}
```
In case the job has already been completed, the API will respond with a message indicating that the job is already done:
```json
{
"message": "Job is already completed."
}
```
:::important
You can only cancel transcoding jobs that are not in a completed status.
:::
---
## Introduction(Transcoding-service)
Welcome to the Transcoding as a Service documentation! This guide is your gateway to mastering our transcoding service, simplifying the process of converting videos into different resolutions. Transcoding is the key to ensuring your videos play smoothly on various devices and streaming platforms.
Refer the documentation below for more information.
- [Transcode a Video](transcode-a-video)
- [List all Transcoding Jobs](list-all-transcoding-jobs)
- [Get Transcoding Job Details](get-transcoding-job-details)
- [Cancel Transcoding Job](transcoding-cancelation)
---
## AES Encryption
Advanced Encryption Standard (AES) is a widely used symmetric encryption algorithm designed to secure data.
It employs a block cipher method, transforming data in fixed-size blocks, typically 128 bits. AES operates through a series of rounds involving substitution, permutation, and mixing of data elements, making it highly resistant to attacks.
It offers key lengths of 128, 192, or 256 bits for varying levels of security. AES encryption is widely adopted for its efficiency and robust protection, used in securing sensitive data during transmission and storage
### Upload AES Encrypted video
To Upload a asset with AES Encryption you need to send an HTTP POST request to the API [Endpoint](../server-api/assets.md#upload-an-video), with the
"content_protection_type" as "aes".
**Sample request body**
```json
{
"title": "Big Buck Bunny Video",
"inputs": [
{
"url": "https://static.testpress.in/BigBuckBunny.mp4"
}
],
"resolutions": ["240p", "360p", "480p", "720p"],
"content_protection_type": "aes",
"folder": "32seYYHeNxE"
}
```
### How to play AES Encrypted video
#### Generate a Signed Playback URL
To successfully access and play AES encrypted videos, it's essential to generate a signed playback URL specifically designed for each video.
To obtain it send an HTTP GET request to the API [Endpoint](../server-api/assets.md#get-individual-asset-details), with the
the query parameter 'expiry,' indicating the duration in seconds until the URL expires.
**Endpoint**
```bash
https://app.tpstreams.com/api/v1//assets//?expiry=100
```
The playback_url provided in the API response is the signed URL that you can use for secure video playback.
**Response**
```json
{
"title": "sample.mp4",
"bytes": null,
"type": "video",
"video": {
....
....
"playback_url": "https://d384padtbeqfgy.cloudfront.net/transcoded_private/B66mmRm2TPF/video.m3u8?Expires=1692398327&Signature=OXqiVfCVAFrNoMq0hrpJ05YYY0XeyzA1H8kD6lv~~71v5PCdpf-9h1Qe~A0RFYoJuOq22j70juEFVJNjEr-WvVPvCvSRoYbRG6xEx5sr-541G~UkBXrcNXRHpb1988hQdG8NAh2pCV6o7bFOKsk3BBk8t6FRo-ZYs6xl46vFN8qH3FCNMhQLFmWqNpNo1vSPjmSSZlNrAplBkNq7MWxoNPxEFrzBgKusqrFZWLqOoXdzR8f9kb9VKkEQAPZL2tk71D6aN8toxwPV70esr8df78hkmAl3d4lChKZlrbWKd0tzew3RDPYZxicxfD1ZBx0th5PQfCltukkitQ0zPbE3TQ__&Key-Pair-Id=K2XWKDWM065EGO",
},
}
```
#### Handle AES-encrypted key request
The player sends a request to our endpoint, as indicated within the m3u8 file, to obtain the AES decryption key needed for video playback. However, the API endpoint mandates an access token for authorization.
To handle this, it's important to ensure that the access token is included in the request as a query parameter initiated by the player.
To generate access tokens send an HTTP POST request to the API [Endpoint](../server-api/access-token.md#create-an-access-token)
**Sample video.js code**
```html
Video.js HLS Example
```
Replace `"PLAYBACK_SIGNED_URL.m3u8"` with the actual playback_url of your encrypted video and `"YOUR_ACCESS_TOKEN_HERE"` with the provided access token.
---
## Playback Authentication
Access token generated using Streams API which is required to authorize video playback.
If the user has your video embed code he can embed your video onto any page on the internet if you don't have domain restrictions. even if you have, users can view the video by embedding your video on your home page or any other page that doesn't restrict users.
You can prevent the above scenario by generating an access token using API with time-to-live or set to expire after one usage on the back-end server and then sent to the website front-end and use generate access_token as part of the video embed code.
Access token CRUD operation APIs are documented [here](../server-api/access-token.md)
---
## Getting started(Video-embedding)
For any video that you host with Streams, if you want you use it somewhere else, you have to embed it. This means that when you have generated the embed code for your video, you will be able to add the video to your website, blog, articles, or other website you’re looking forward to.
In order to allow embedding for a certain video, you will first need to follow the below steps which will guide you through the whole Video Embedding process
### Generate an embed code
Construct iframe source URL:
```bash
https://app.tpstreams.com/embed/{{asset_id}}/?access_token={{access_token}}/
```
** Sample format **
```html
```
### Embed on your site
Now you use constructed iframe code in your code to embed the video.
** Example **
```html live showLineNumbers
```
### Live Viewer Count
TPStreams displays the number of viewers currently watching a live stream.
The viewer count is shown above the player on the live stream page.

By default the player identifies a viewer with a cookie it sets on the embed page, so the count measures devices rather than people.
| Situation | Counted as |
| ----------- | ----------- |
| Two tabs in the same browser | 1 |
| Two different browsers on one device | 2 |
| The same person on a phone and a laptop | 2 |
Counting devices means nobody can inflate the number by opening more tabs, but it reads higher than your real audience when people watch on more than one device.
To count people instead, add the `viewer_id` parameter to the embed URL.
```bash
https://app.tpstreams.com/embed/{{asset_id}}/?access_token={{access_token}}&viewer_id={{your_user_id}}
```
**Fields**
| Name | Type | Description | Required |
| ----------- | ----------- | ----------- | ---------- |
| viewer_id | string | The identifier your own system uses for the signed-in user. The same person is then counted once across every device they watch on | No |
:::important
- Use the identifier your own system already has for that user, so it stays the same across sessions and devices. A value that changes per session counts one person many times.
- Use an opaque ID rather than an email address or a name. The value travels in the embed URL, and URLs end up in browser history, referrer headers and server logs.
- The value is hashed and scoped to your organization before it is used for counting, so Streams does not hold the raw ID and the same ID in two organizations is never confused.
:::
This parameter affects only the live viewer count. It has no effect on recorded videos, playback or authorization.
---
## Custom Overlays
The Custom Overlay feature allows you to display interactive HTML content over the video player at specific times or on demand. Typical use cases include:
- Quizzes
- Polls / Voting
- Feedback forms
- Call-to-action buttons
- Announcements / Informational content
Overlays can appear automatically at a timestamp or be triggered programmatically via `player.showOverlay()`.
#### Overlay Configuration
| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `id` | string | Yes | Unique identifier for the overlay |
| `contentHtml` | string | No | HTML content to display in the overlay |
| `showAt` | number | No | Video timestamp (seconds) to show overlay |
| `hideAt` | number | No | Video timestamp (seconds) to hide overlay |
| `className` | string | No | CSS class name for custom styling |
| `verticalAlign` | 'top' \| 'center' \| 'bottom' | No | Vertical position (default: 'center') |
| `horizontalAlign` | 'left' \| 'center' \| 'right' | No | Horizontal position (default: 'center') |
**Default behavior**
- If `showAt` is not set, the overlay appears immediately.
- If `hideAt` is not set, it stays until user interaction.
- If `className` is not set, default styling is applied.
- Alignment defaults to center.
#### Methods
**showOverlay**
Displays an overlay over the video player.
```js
player.showOverlay({
id: 'quiz-overlay',
contentHtml: '
Content here
',
showAt: 30,
hideAt: 60,
className: 'custom-overlay'
});
````
#### Events
**onOverlayShow**
Triggered when an overlay becomes visible.
```js
player.on('onOverlayShow', id => {
console.log('Overlay shown:', id);
});
```
**onOverlayHide**
Triggered when an overlay is hidden or dismissed.
```js
player.on('onOverlayHide', id => {
console.log('Overlay hidden:', id);
});
```
**overlayAction**
Triggered when a user interacts with elements inside an overlay (buttons, form submissions, etc.).
```js
player.on('overlayAction', data => {
console.log('Button clicked:', data.value, 'in overlay:', data.id);
});
```
**Event Data**
```js
{
type: 'overlayAction',
id: overlayId,
value: buttonValue
}
```
#### Styling
Default overlays cover the full video and center the content. You can customize appearance using `className`.
```css
.quiz-overlay {
background: rgba(0,0,0,0.8);
color: white;
padding: 20px;
border-radius: 10px;
}
.cta-overlay button {
background: #007bff;
color: white;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
margin: 5px;
}
.poll-overlay label {
display: flex;
align-items: center;
padding: 10px 15px;
margin-bottom: 10px;
background: #f0f0f0;
border-radius: 6px;
cursor: pointer;
transition: background 0.2s;
}
```
1. Use unique overlay IDs to prevent conflicts.
2. Use `data-value` attributes on buttons for easy event handling.
3. Test timing (`showAt` / `hideAt`) to ensure good UX.
4. Keep content concise and mobile-friendly.
5. Ensure accessibility: proper contrast, keyboard navigation.
For detailed examples of quizzes, polls, feedback forms, and announcements with custom styling, check:
[Player Overlay Examples](https://github.com/testpress/player-overlay-examples)
---
## About player SDK
Our player SDK enables you to interact with embedded Streams players through the code on your web page. You can modify the default behavior of the player for features like looping, execute custom functions on particular playback events, and even set basic properties like the volume, and playback rate of the player.
The SDK, as a JavaScript library, is completely separate from the API. You connect to it differently, and it gives you a different range of options.
---
## Player Events(Javascript-sdk)
### About Player events
You can listen for events in the player by attaching a callback using .on()
```js
player.on('ended', function() {
// Executes when the video is ended
});
```
The events are equivalent to the HTML5 video events.
To remove a listener, call .off() with the callback function. If you pass an event name only, you remove all listeners for that event.
```js
var onPlay = function() {
};
player.on('ended', onPlay);
// If later you decide that you don't need to listen for `ended`
player.off('ended', onPlay);
// Alternatively, call `off` with just the event name to remove all listeners
player.off('ended');
```
### Events for playback controls
- ended
- error
- loaded
- pause
- play
- progress
- seeked
- timeupdate
- volumechange
- ratechange
#### ended
This event fires when playback reaches the end of a video.
:::info
When the player's loop behavior is enabled, the ended event doesn't fire.
:::
#### loaded
This event fires when a new video is loaded in the player.
#### volume
This event fires when the volume in the player changes.
#### timeupdate
This event fires when the playback position of the video changes, generally every 250 ms during playback, but the interval can vary depending on the browser.
#### seeked
This event fires when the player seeks a specific time. A simultaneous timeupdate event also fires.
#### progress
This event fires while the video is loading. The event data indicates the amount of the video that has been buffered.
#### play
This event fires when the video plays.
#### pause
This event fires when the video is paused.
#### error
This event fires when the player experiences some sort of error. If a method call generated the error, the name of the method appears in the event data, along with the name of the error.
#### ratechange
This event fires when the playback rate of the video in the player changes.
---
## Player Methods(Javascript-sdk)
#### About player methods
You can call player methods by calling the corresponding function on the Player object. All player method executes only after the iframe is loaded, so no need to wait for the player to loaded to call the methods.
``` js
player.play();
```
All methods, except for on() and off(), return a Promise — a special JavaScript object that stands for the result of the operation, whether success or failure.
Promises for methods that return information resolve with the value of the property in question.
```js
player.getLoop().then(function(loop) {
// Whether the player is set to loop
});
```
#### Get the playback position of a video
This method gets the current playback position of a video, measured in seconds.
**Function**
```js
getCurrentTime()
```
**Returns**
```bash
Promise
```
**Example**
``` bash
player.getCurrentTime().then(function(seconds) {
// `seconds` indicates the current playback position of the video
});
```
#### Get the loop state of a player
This method gets the loop state of a player, where true indicates that the video restarts once it reaches the end of playback.
**Function**
```js
getLoop()
```
**Returns**
```bash
Promise
```
**Example**
```js
player.getLoop().then(function(loop) {
// `loop` indicates whether the loop behavior is active
});
```
#### Get the pause state of a player
This method gets the pause state of the current player, where true indicates that playback is paused.
**Function**
```js
getPaused()
```
**Returns**
```bash
Promise
```
**Example**
```js
player.getPaused().then(function(paused) {
// `paused` indicates whether the player is paused
});
```
#### Get the ended state of a video
This method gets the ended state of the video, where true indicates that the video has ended. The video has ended if its current playback position is exactly equal to its duration.
**Function**
```bash
getEnded()
```
**Returns**
```bash
Promise
```
**Example**
```js
player.getEnded().then(function(ended) {
// `ended` indicates whether the video has ended
});
```
#### Get the playback rate of a player
This method gets the playback rate of a player on a scale from 0.5 to 2, where 0.5 is half speed and 2 is double speed.
**Function**
```js
getCurrentPlaybackRate()
```
**Returns**
```bash
Promise
```
**Example**
```js
player.getCurrentPlaybackRate().then(function(playbackRate) {
// `playbackRate` indicates the numeric value of the current playback rate
});
```
#### Get the volume level of a player
This method gets the volume level of a player on a scale of 0 to 1.
:::info
Most mobile devices don't support a volume level independent of the system volume. In these cases, this method always returns 1.
:::
**Function**
```js
getVolume()
```
**Returns**
```bash
Promise
```
**Example**
```js
player.getVolume().then(function(volume) {
// `volume` indicates the volume level of the player
});
```
#### Get the video duration
This method gets the current video duration in seconds.
**Function**
```js
getDuration()
```
**Returns**
```bash
Promise
```
**Example**
```js
player.getDuration().then(function(duration) {
// duration 596.4630000000002
});
```
#### Get user watched time ranges
This methods returns the list of time ranges that user watched in a video.
**Function**
```js
played()
```
**Returns**
```bash
Promise
```
**Example**
```js
player.played().then(function(ranges) {
// ranges => [[0, 120.084666], [155.292928, 161.054032], ...]
});
```
#### Pause a video
This method pauses the playback of a video.
**Function**
```js
pause()
```
**Returns**
```bash
Promise
```
**Example**
```js
player.pause().then(function() {
// The video is paused
}).catch(function(error) {
// Some other error occurred
});
```
#### Play a video
This method plays the playback of a video.
**Function**
```js
play()
```
**Returns**
```bash
Promise
```
**Example**
```js
player.play().then(function() {
// The video is paused
}).catch(function(error) {
// Some other error occurred
});
```
#### Set the playback position of a video
This method sets the current playback position in seconds. The player attempts to seek to as close to the specified time as possible. The exact time comes back as the fulfilled value of the promise.
**If playback hasn't started yet**, using this method starts playback.
**If playback has already started**, using this method doesn't affect the play state.
:::info
If the player is paused, it remains paused. If the player is playing, it buffers the video from the new position and then resumes playing.
:::
**Function**
```js
setCurrentTime(seconds)
```
**Parameters**
|Parameter| Data type | Required? | Description |
|---------| ----------|-----------|-------------|
| seconds | Number | Yes | The playback position in seconds.|
**Returns**
```bash
Promise
```
**Errors**
| Error | Description |
| ------ | ----------- |
| RangeError | The time is less than 0 or greater than the video's duration.|
| Error | Some other error occurred. |
**Example**
```js
player.setCurrentTime(30.456).then(function(seconds) {
// `seconds` indicates the actual time that the player seeks to
}).catch(function(error) {
switch (error.name) {
case 'RangeError':
// The time is less than 0 or greater than the video's duration
break;
default:
// Some other error occurred
break;
}
});
```
#### Set the autoplay state of a player or browser
This method enables or disables autoplay in a player or browser, where true indicates that autoplay is enabled. Under autopause, whenever a new video loads in the browser window, the video begins in a played state.
By default autoplay was disabled.
:::info
The autopause feature has no effect if you've disabled cookies in your browser, either through browser settings or with an extension or plugin.
:::
**Function**
```js
setAutoPlay({autoplay})
```
**Parameters**
| Parameter | Data type | Required? | Description |
| --------- | --------- | --------- | ----------- |
| autopause | Boolean | Yes | The autopause state to set.|
**Returns**
```bash
Promise
```
**Errors**
| Error | Description |
| --------- | --------- |
| UnsupportedError | Autopause isn't supported by the current player or browser. |
| Error | Some other error occurred. |
**Example**
```js
player.setAutoPlay(false).then(function(autopause) {
// Autoplay is disabled
}).catch(function(error) {
// Handle errors
});
```
#### Set the loop state of a player
This method sets the loop state of the player. When the loop state is true, playback resumes at the beginning of the video immediately after the video ends.
**Function**
```js
setLoop(loop)
```
**Parameters**
| Parameter | Data type | Required? | Description |
| --------- | --------- | --------- | ----------- |
| loop | Boolean | Yes | Whether the player loops video playback. |
**Returns**
```bash
Promise
```
**Example**
```js
player.setLoop(true).then(function(loop) {
// The loop behavior is enabled
});
```
#### Set the playback rate of a player
This method sets the playback rate of the player on a scale from 0.5 to 2, where 0.5 is half speed and 2 is double speed. When you set the playback rate through the API, the specified value isn't synchronized to other players or stored as the viewer's preference.
**Function**
```js
setPlaybackRate(playbackRate)
```
**Parameters**
| Parameter | Data type | Required? | Description |
| --------- | --------- | --------- | ----------- |
| playbackRate| Number | Yes| The playback rate of the player from 0.5 to 2.|
**Returns**
```bash
Promise **
```
**Errors**
| Error | Description |
| --------- | --------- |
| RangeError | The playback rate is less than 0.5 or greater than 2. |
| Error | Some other error occurred.|
**Example**
```js
player.setPlaybackRate(0.5).then(function(playbackRate) {
// The playback rate is set
}).catch(function(error) {
switch (error.name) {
case 'RangeError':
// The playback rate is less than 0.5 or greater than 2
break;
default:
// Some other error occurred
break;
}
});
```
#### Set the volume level of a player
This method sets the volume level of the player on a scale from 0 to 1. When you set the volume through the API, the specified value isn't synchronized to other players or stored as the viewer's preference.
**Function**
```js
setVolume(volume)
```
**Parameters**
| Parameter | Data type | Required? | Description |
| --------- | --------- | --------- | ----------- |
| volume | Number |Yes | The volume level of the player from 0 to 1. |
**Returns**
```bash
Promise
```
**Errors**
| Error | Description |
| --------- | --------- |
| RangeError | The volume is less than 0 or greater than 1. |
| Error | Some other error occurred. |
**Example**
```js
player.setVolume(0.5).then(function(volume) {
// The volume is set
}).catch(function(error) {
switch (error.name) {
case 'RangeError':
// The volume is less than 0 or greater than 1
break;
default:
// Some other errors occurred
break;
}
});
```
#### Apply watermark to the video
This method applies watermark to the video. Please refer to this [doc](../watermarking.md) for more details on watermarking.
**Function**
```js
applyWatermark()
```
**Parameters**
| Parameter | Data type | Required? |
| --------- | --------- | --------- |
| annotations | json |Yes |
**Returns**
```bash
Promise
```
**Example**
```js
const annotations = [
{
"type":"dynamic",
"text":"Dinesh",
"opacity":"0.8",
"color":"#FF0000",
"size":"5",
"interval":5000,
"skip": 2000
}
]
player.applyWatermark(annotations).then(function() {
// The watermark is applied
})
```
#### Enable resume video playback
Assigning a unique ID to a user automatically enables the resume video playback feature across all devices. The user's progress will be saved periodically, ensuring playback resumes from the last saved point when they return.
**Function**
```js
setPlayerUserId()
```
**Returns**
```bash
Promise
```
**Example**
``` bash
player.loaded().then(()=>{
player.setPlayerUserId("shantanu@testpress.in")
});
```
#### Get the watched duration of a video
This method retrieves the total watched duration of a video, measured in seconds.
##### **Function**
```js
getWatchedTime()
```
##### **Returns**
```bash
Promise
```
##### **Example**
```js
player.getWatchedTime().then(function(duration) {
console.log(duration, "user watched duration");
});
```
This will log the total duration the user has watched the video.
#### Set custom error messages
This method allows you to customize error messages for specific error types. If a custom message is not provided for an error type, the system's default error message will be used.
**Function**
```js
setCustomErrorMessage(errorMessages)
```
**Parameters**
| Parameter | Data type | Required? | Description |
| --------- | --------- | --------- | ----------- |
| errorMessages | Object | Yes | An object containing custom error messages for specific error types. |
**Supported Error Types**
| Error Type | Description |
| ------------------------- | ----------- |
| BrowserNotSupported | Error when the browser is not supported. |
| DRMKeySystemUnsupported | Error when the DRM key system is not supported. |
| NetworkNotAvailable | Error when the network is not available. |
| DefaultPlaybackError | Default error message for general playback errors. |
**Returns**
```bash
Promise
```
**Example**
```js
player.setCustomErrorMessage({
'BrowserNotSupported': 'This browser is not supported.',
'DRMKeySystemUnsupported': 'A DRM-related error has occurred',
'NetworkNotAvailable': 'A network error occurred. Please check your internet connection.',
'DefaultPlaybackError': 'An unknown playback error occurred.'
}).then(function() {
// Custom error messages are set
});
```
:::info
Only the four error types listed above can be customized. If you don't provide a custom message for a specific error type, the system's default error message will be used.
:::
---
#### Set custom DRM license endpoint
This method allows you to configure a custom DRM license endpoint for the player.
By default, the embedded player directly calls the platform’s DRM license API. Using this method, API clients can instead route DRM license requests through their own endpoint, which can act as a DRM proxy.
This enables clients to:
* Inject additional DRM configuration or policies
* Customize request payloads before forwarding them
* Extend DRM behavior without changing the existing license contract
The configured endpoint receives the DRM challenge from the player, processes or enriches the request as needed, forwards it to the platform’s DRM license API, and returns the license response back to the player.
**Function**
```js
setDRMURL(url)
```
**Returns**
```bash
Promise
```
**Example**
```js
player.setDRMURL("https://client.example.com/drm-proxy").then(function() {
// Custom DRM endpoint is configured
}).catch(function(error) {
// Handle errors
});
```
---
#### Clear custom error messages
This method clears all previously set custom error messages. After calling this method, the player will use the system's default error messages for all error types.
**Function**
```js
clearCustomErrorMessage()
```
**Returns**
```bash
Promise
```
**Example**
```js
player.clearCustomErrorMessage().then(function() {
// Custom error messages are cleared, system defaults will be used
});
```
#### Request Fullscreen
This method requests the player to enter native fullscreen mode.
If the browser allows it, the video will expand to fullscreen.
**Function**
```js
requestFullscreen()
```
**Returns**
```bash
Promise
```
**Example**
```js
player.requestFullscreen();
```
#### Exit Fullscreen
This method requests the player to exit native fullscreen mode.
If the player is currently in fullscreen, it will return to its embedded state.
**Function**
```js
exitFullscreen()
```
**Returns**
```bash
Promise
```
**Example**
```js
player.exitFullscreen();
```
#### Enable Fullscreen Toggle Visibility
This method shows the fullscreen toggle button in the player’s control bar.
After calling this method, users will be able to enter or exit fullscreen using the player UI.
**Function**
```js
enableFullscreen()
```
**Returns**
```bash
Promise
```
**Example**
```js
player.enableFullscreen();
```
#### Disable Fullscreen Toggle Visibility
This method hides the fullscreen toggle button from the player’s control bar.
After calling this method, fullscreen can still be controlled programmatically, but not via the player UI.
**Function**
```js
disableFullscreen()
```
**Returns**
```bash
Promise
```
**Example**
```js
player.disableFullscreen();
```
---
## Player Parameters
#### About Player Parameters
Player parameters are query parameters that you can append to the `playback_url` to configure the player's behavior. These parameters allow for a customized playback experience by controlling features such as mute, autoplay, looping, and more directly through the URL.
#### Parameters Overview
The following query parameters can be added to the `playback_url` to control the player's settings:
| Parameter | Data Type | Values | Description |
|---------------|-----------|--------------------------------|-----------------------------------------------------------------------------|
| `background` | String | `'1'` or `'0'` | Enables or disables background mode. `'1'` enables it, `'0'` disables it. |
| `muted` | String | `'1'` or `'0'` | Mutes or unmutes the player. `'1'` mutes the player, `'0'` unmutes it. |
| `autoplay` | String | `'1'` or `'0'` | Controls autoplay. `'1'` enables autoplay, `'0'` disables it. |
| `loop` | String | `'1'` or `'0'` | Enables or disables loop mode. `'1'` enables looping, `'0'` disables it. |
| `playRates` | String | Comma-separated values (e.g., `"0.5,1,2,3"`) | Sets available playback speed options. Minimum value: `0.5`, Maximum value: `4`. |
#### Usage
These parameters are appended to the `playback_url` in the format `key=value`. Multiple parameters can be included by separating them with an ampersand (`&`).
##### Example Usage
```bash
https://app.tpstreams.com/embed/dcek2m/BxDe5ZYDyD6/?access_token=72186a33-107a-49b6-b275-a853c907be33&muted=1&autoplay=1&loop=1&playRates=0.5,1,2,3
```
#### Detailed Description
- **`background`**: Controls whether the player operates in background mode, useful for scenarios where the video should continue playing in the background.
- **`muted`**: Sets whether the video should start muted. If set to `'1'`, the video will begin without sound, which is especially useful in autoplay scenarios.
- **`autoplay`**: Determines whether the video will start playing as soon as the player is ready. Setting this to `'1'` enables autoplay.
- **`loop`**: Defines whether the video will automatically restart from the beginning after it finishes. A value of `'1'` enables looping.
** Sample format **
```html
```
### Embed on your site
Now you use constructed iframe code in your code to embed the video.
** Example **
```html live showLineNumbers
---
## Markers
### Enable plugin
You can enable a plugin by passing query param **plugins** with value "markers to src URL"
### Initialize markers
You can intialize markers through [Player SDK](../using-player-sdk.md) method call setMarkers
** Arguments **
| Name | Type | description |
| ----------- | ----------- | ----------- |
| markerDisplay | Object | The general css styling that would be applied to all the markers created by the plugin. The default is only concerned with width, border-radius, and background-color, but any other css rules would be applied too (think of it as jQuery's css function).
| breakOverlay | Object | Options for the break overlays at each marker. Available fields => display, displayTime, style |
You can read more about available arguments [here](http://sampingchuang.com/videojs-markers)
** Events **
| Name | description |
| ----------- | ----------- |
| onMarkerReached | This callback function is triggered whenever playback reaches the time interval from a marker. The interval is specified in the breakOverlay.displayTime . This can be used to trigger certain actions at each marker, depending on your use case. Again, this function is given a marker instance. In 0.6.0, the function also takes in a second parameter index. |
| onMarkerClick | This callback function is triggered when clicking on the markers. The default behavior for clicking on the marker is seeking to that point in the video. However, if onMarkerClick returns false, the default behavior will be prevented. |
### Methods
**Remove markers**
You can remove the particular markers by passing its indices to removeMarkers function.
``` js
player.removeMarkers([1,2,4]);
```
**Remove All markers**
``` js
player.removeAllMarkers([1,2,4]);
```
### Sample code
``` html
```
#### Output

---
## Using Player SDK
The iframe embed lets you embed your Streams videos and control the player using Javascript. You have access to the essential methods and properties of the players. There are events that you can listen for and execute custom actions in your web application.
#### Adding the script
Add the following script to the html of your web page. This loads the interface that are used to establish communication with the video player. If this script is loaded on-demand later, make sure to wait for the load to complete before calling subsequent methods on the object.
```js
```
#### Get a reference to the iframe
Note: Assuming the API script (above) is already loaded,
To begin communicating with the player, get a reference to the iframe element. This can be using DOM APIs such as querySelector().
```js
const iframe = document.querySelector("iframe");
```
#### Establish communication with the iframe
```html
page_title
```
#### What's next
It's time to start controlling some videos. We've compiled a comprehensive reference of [methods](./player-methods) and [events](./player-events) to make your dreams of absolute power a reality.
If we've confused you going forward, contact us. We tend to get it right the second time.
---
## Shaka Player Integration Guide
This guide will walk you through integrating Shaka Player for playing TPStreams Non-DRM & multi-DRM-protected videos on your site.
#### Why Use Shaka Player?
- Adaptive Streaming: Automatically adjusts video quality based on network conditions.
- Multi-DRM Support: Supports major DRM systems like Widevine, and FairPlay.
- Cross-Browser Compatibility: Works across all modern browsers.
- Open Source: Free to use and backed by Google.
#### Setting Up Non-DRM Video Playback
**1.1 Set Up the Basic HTML Structure**
Create an HTML file, include the Shaka Player library and add a video element to play the video.
``` html
```
##### 1.2 Initialize the Shaka Player
Initialize a new instance of the Shaka Player and then attach it to the video element in your HTML and provide the manifest URL of your non-DRM video to the Shaka Player.
```javascript
// You can obtain the M3U8 URL either through our portal or using our Asset Detail API.
const manifestUri ='';
function initApp() {
// Install built-in polyfills to patch browser incompatibilities.
shaka.polyfill.installAll();
if (shaka.Player.isBrowserSupported()) {
initPlayer();
} else {
console.error('Browser not supported!');
}
}
async function initPlayer() {
const video = document.getElementById('video');
const player = new shaka.Player();
await player.attach(video);
// Attach player to the window to make it easy to access in the JS console.
window.player = player;
try {
await player.load(manifestUri);
console.log('The video has now been loaded!');
} catch (e) {
onError(e);
}
}
function onErrorEvent(event) {
onError(event.detail);
}
function onError(error) {
console.error('Error code', error.code, 'object', error);
}
document.addEventListener('DOMContentLoaded', initApp);
```
#### Setting Up Multi-DRM Video Playback
Now, let's extend our setup to support DRM-protected content. We will cover both Widevine and FairPlay DRM systems.
##### 2.1 Load Manifest URL
Determine browser type using the user agent string to select either HLS (m3u8) for Safari or DASH (mpd) for others, ensuring compatibility with various DRM systems. You can obtain the both M3U8 and MPD file URLs via our Asset Detail API.
##### 2.2 Generate DRM License URLs
To obtain DRM license URLs for both Widevine and FairPlay, follow these steps:
Generate an access token granting access to the TPStreams License API. Refer to the provided [documentation](./authentication) for instructions.
Use the following endpoint to construct the license URLs for both Widevine and FairPlay:
```bash
https://app.tpstreams.com/api/v1//assets//drm_license/?access_token=&drm_type=
```
Replace placeholders with actual values:
- ``: Your organization's ID
- ``: ID of the asset
- ``: Access token obtained in above step.
- ``: Use "widevine" for Widevine or "fairplay" for FairPlay.
Incorporate the generated license URLs into your Shaka Player configuration for Widevine and FairPlay DRM support.
##### 2.3 Implement Widevine DRM Support
Specify the license server URL in the DRM configuration to enable Widevine DRM:
```javascript
player.configure({
drm: {
servers: {
'com.widevine.alpha': WIDEVINE_LICENSE_URL,
},
},
});
```
Register request filters with Shaka Player's networking engine to modify license requests:
```javascript
player.getNetworkingEngine().registerRequestFilter(function (type, request) {
if (type == shaka.net.NetworkingEngine.RequestType.LICENSE) {
// This ensures that the license request contains the appropriate content type,
// which is required by our API
request.headers['Content-type'] = 'application/octet-stream';
}
});
```
##### 2.4 Implement FairPlay DRM Support
Retrieve the FairPlay DRM certificate from a specified URL before playback. This certificate is essential for decrypting the video content.
```javascript
const response = await fetch(CERTIFICATE_PATH);
const certificate = await response.arrayBuffer();
```
Configure Shaka Player to use FairPlay DRM by specifying the license server URL and the fetched certificate in the DRM configuration.
```javascript
player.configure({
drm: {
servers: {
'com.apple.fps.1_0': FAIRPLAY_LICENSE_URL,
},
advanced: {
'com.apple.fps.1_0': {
serverCertificate: new Uint8Array(certificate),
},
},
},
});
```
Modify the initialization data for FairPlay by extracting the content ID and adjusting the license URL. This ensures proper handling of the video content by the FairPlay DRM system.
```javascript
player.configure('drm.initDataTransform', (initData, type, drmInfo) => {
if (type != 'skd') return initData;
const contentId = new TextDecoder("utf-16").decode(initData.slice(16));
const cert = player.drmInfo().serverCertificate;
return shaka.util.FairPlayUtils.initDataTransform(
initData,
contentId,
cert
);
});
```
Register request filters with Shaka Player's networking engine to modify license requests. For FairPlay, set the appropriate headers and payload for the license request.
```javascript
player.getNetworkingEngine().registerRequestFilter(function (type, request) {
if (type == shaka.net.NetworkingEngine.RequestType.LICENSE) {
request.uris = [FAIRPLAY_LICENSE_URL];
request.method = 'POST';
request.headers['Content-Type'] = 'application/json';
const originalPayload = new Uint8Array(request.body);
const base64Payload = shaka.util.Uint8ArrayUtils.toStandardBase64(originalPayload);
request.body = JSON.stringify({
spc: base64Payload,
});
}
});
```
#### Setting Up Offline Download Support
Shaka Player supports offline playback, enabling users to download and play DRM-protected videos without an active internet connection.
- Refer to the Shaka Player [documentation](https://shaka-player-demo.appspot.com/docs/api/tutorial-offline.html) for implementing offline support.
- When configuring DRM settings for offline download, follow the steps outlined earlier to construct the DRM license URL. Additionally, ensure to include the query parameter download=true to acquire a persistent license suitable for offline use.
For complete sample code, refer to our sample [repository](https://github.com/testpress/shaka-player-demo/).
---
## Watermark Videos
Videos hosted through Streams cannot be downloaded. There does however remain the risk of piracy from screen capture. Add text to videos with our dynamic watermark feature that effectively prevents users from pirating video content using screen capture, and goes a long way towards helping users protect their premium content.
The dynamic watermark can be customized for movement, color, size and transparency.
### Create Watermark Code
Here is a sample JSON string that adds a moving (dynamic) watermark and a static watermark.
```js
{
"annotations":[
{
"type":"static",
"text":"Testpress",
"x":10,
"y":10,
"opacity":"0.5",
"color":"#FFF",
"size":6
},
{
"type":"dynamic",
"text":"hari",
"opacity":"0.5",
"color":"#FF0000",
"size":6,
"interval":5000,
"skip":2000
}
]
}
```
Technically, this is an array of JSON objects, where each object describe a single annotation item.
Each of these items will be described by its parameters. Every item requires a **type** parameter which defines the type of watermark by default its value was static. The type of watermark can be either a moving text or a static text. The rest of the parameters depends on the type.
Following is a short description of how each parameter affects the display of text.
#### Static text
The following code will display a static watermark code, placed at 10px distance from the left border of the video and 50px from top border, displaying text **Testpress** . The text color will be white (#fff), opacity is 0.5, and font-size is 6.
```js
[{
"type": "static",
"text": "Testpress",
"x": 10",
"y": 10,
"opacity": "0.5",
"color": "#FFF",
"size": "6"
}]
```
#### Moving text
The following code will display a dynamic watermark code, displaying text **hari**. The text color will be red (#ff0000), opacity is 0.8, and font-size is 6. The watermark is configured to update position every 5 seconds (5000ms).
```js
[{
'type': 'dynamic',
'text': 'hari',
'opacity': '0.8',
'color': '#FF0000',
'size': '6',
'interval': 5000,
'skip': 2000,
}]
```
**Type of text**
Set type parameter as dynamic for Dynamic watermark and static for Static watermark
```
'type':'dynamic',
```
**Set the text to be shown**
```
'text" : 'testpress',
```
**Specify text opacity**
This is the opacity of the text. For full opacity keep value 1.
```
'opacity':'0.8',
```
**Specify text color**
This is the hex value of the watermark text color.
```
'color':'#FF0000',
```
**Specify the font size**
This is the font size
```
'size':6,
```
**Specify the interval over which watermark changes position**
The value is the interval in milliseconds when the text changes position
```
'interval':5000,
```
**Skip feature for watermark**
It is possible to have watermark skip for some time between two overlays. Here is a sample code for it –
```
'skip':2000
```
### Apply Watermark
Now you just need pass the watermark code that you've created to the player to apply on the video. There are two ways to do that
- Pass the code as part of the [Access token Request](../server-api/access-token.md#create-an-access-token).
- Pass the code via Player SDK method called [applyWatermark](./javascript-sdk/player-methods#apply-watermark-to-the-video).
---
## Video
## Video Documentation
Welcome to the Video documentation section. Select one of the areas below to get started:
Video Embedding
Learn how to embed our video player on your website or application. This section covers player parameters, customizing playback behavior, event tracking, and integrations with custom players like Shaka Player.
Get Started
Videos Uploader
Learn how to integrate our video uploader. You can choose between the Embedded Uploader widget with built-in UI, or use the Javascript Uploader SDK for a fully customized uploading workflow.
Get Started
---
## Embedded Uploader
#### Introduction
The TPStreams Uploader allows you to embed a powerful video upload tool directly on your website. With this integration, you can upload videos directly to your TPStreams account without needing to navigate away from your site, making the experience smooth and efficient. Whether you're embedding the uploader on a standalone page or in a modal, this guide will walk you through each step to get it up and running.

### Guide to integrate the uploader
This guide will help you integrate the TPStreams video uploader into your website allowing you to upload videos directly to your TPStreams account.
#### Step 1: Get the Authentication Token
Use the API mentioned [here](../server-api/authentication.md) to obtain a user authentication token. This token is required for the uploader to authenticate uploads.
#### Step 2: Import the TPStreams Uploader SDK
Include the TPStreams Uploader SDK script in your webpage. Add the following `
```
#### Step 3: Add the TPStreams Uploader HTML Element
Place the following HTML tag where you want the uploader to appear on your webpage, including any modals if needed.
Make sure to replace the organization-id attribute with your own TPStreams organization ID.
```html
```
Note: This tag also supports `width` and `height` attributes if you need to customize the dimensions of the uploader UI.
#### Step 4: Initialize the Uploader
When you are ready to display the uploader, execute the following JavaScript to initialize it with the authentication token:
```html
```
#### Full Example Code
The following code will display the TPStreams uploader UI on your webpage, allowing users to upload videos directly to your account.
```html
```
#### Domain Restrictions for Embedding
In TPStreams settings, you can restrict the allowed domains that can embed this uploader. Set the allowed domain(s) to ensure only specified sites can use your uploader. If no domain is specified, all domains are permitted by default.

### Uploader Events
You can listen for events in the uploader by attaching a callback using .on()
#### fileUploaded
This event fires when a video is successfully uploaded to TpStreams.
```html
```
---
## Javascript Uploader
#### Introduction
If you would like to upload videos without using the embedded uploader UI, you can use our javascript SDK that provides methods to upload videos, and track its progress.
### Guide to integrate the uploader
This guide will help you integrate the TPStreams video uploader SDK into your website allowing you to upload videos directly to your TPStreams account.
#### Step 1: Get the Authentication Token
Use the API mentioned [here](../server-api/authentication.md) to obtain a user authentication token. This token is required for the uploader to authenticate uploads.
#### Step 2: Import the TPStreams Uploader SDK
Include the TPStreams Uploader SDK script in your webpage. Add the following `
```
#### Step 3: Initialize the uploader instance
Make sure to replace the organization id with your own TPStreams organization ID. Click [here](/docs/videos-uploader/javascript-uploader#uploader-configuration-options) to know more about the uploader configuration options.
```html
```
#### Step 4: Select files and start upload
After you've created a file input on your webpage, you can pass those files to the uploader instance.
You can pass an optional folder id argument to `upload` method if you'd like to upload the videos to a specific folder.
```html
```
#### Full Example Code
The following code will display the video upload progress and asset ID on your webpage.
```html
Video Upload with JS SDK
Upload Video Using TpStreams Uploader SDK
```
### Uploader Configuration Options
You can pass the following configuration options to the uploader while initializing using the `uploaderConfig` argument.
**uploaderConfig**
| Parameter | Description | Type |
|-------------|-------------------------|-----------------|
| generateSubtitle | Auto-generate english subtitles for $0.071 per minute of video. False by default. | Boolean |
| contentProtectionType | Choose encryption standard to safeguard your videos. DRM encryption is enabled by default. | String ("drm", "aes" or "disabled") |
| resolutions | Choose the resolutions in which you want to transcode the videos. By default they will be transcoded in all available resolutions. | List of string ["240p", "360p", "480p", "720p"] |
### Uploader Events
You can listen for events in the uploader by attaching a callback using .on()
#### uploadProgress
Listen to this event to track video upload progress.
```html
```
#### uploadSuccess
This event is fired when a video is successfully uploaded to TpStreams.
```html
```
#### uploadError
This event is fired when an error occurs while uploading a video to TpStreams.
```html
```