Processes videos uploaded to selected Payload upload collections, transcodes them into multiple resolutions and generates HLS segment playlists plus a master manifest. Work runs through the Payload job queue with a configurable concurrency limit, and the manifest location is saved back to the source collection. Requires Payload 3 with a server URL set and ffmpeg available on the server.
Payload plugin for automatic adaptive bitrate process of videos.
Installation
npm i plugin-adaptive-bitrate-videos21
Stars
3
Forks
2
Open Issues
5 months ago
Last Updated
0
Weekly Downloads
v3.1.1
npm Version
117.5 KB
Unpacked Size
3
Dependencies
Trends appear after 7 days of data. Daily snapshots have not started yet.
Trends appear after 7 days of data. Daily snapshots have not started yet.
Each point is npm's rolling 7-day download count.
Health Score
Composite score based on GitHub activity, stars, npm downloads, publish recency, dependency count, and package size.
Badge for your README
[](https://payloaddirectory.dev/plugins/846425506-root)<a href="https://payloaddirectory.dev/plugins/846425506-root"><img src="https://payloaddirectory.dev/badge/846425506-root.svg" alt="Payload Directory"></a>README
This plugin extends Payload CMS to provide Adaptive Bitrate (ABR) streaming capabilities for uploaded video files. It automatically processes video uploads, creates segment playlists for multiple resolution versions, generates HLS manifests, and updates the collection with the master manifest file location.
Updated for Payload 3.0
yarn add plugin-adaptive-bitrate-videos
or
npm install plugin-adaptive-bitrate-videos
Add this package into your dependencies executing this code in your command line:
yarn add plugin-adaptive-bitrate-videos
Now install this plugin within your Payload as follows:
//payload.config.ts
import { buildConfig } from 'payload/config'
import path from 'path'
import { abrVideos } from 'plugin-adaptive-bitrate-videos`'
export default buildConfig({
serverUrl: 'https://example.com' // Must be set to use pluggin
plugins: [
abrVideos({
collections: {
'my-collection-slug': {keepOrginal: true}
}
})
]
// The rest of your config goes here
})
See Payload config options for documentation on setting serverUrl in Payload config.
This plugin can be used with the Payload Cloud Storage Plugin to store you segments and manifest files. cloudStorage is CollectionOptions object from Payload Cloud Plugin Collection specific options.
//payload.config.ts
import { buildConfig } from 'payload/config'
import path from 'path'
import { abrVideos } from 'plugin-adaptive-bitrate-videos'
import { gcsStorage } from '@payloadcms/storage-gcs'
const adapter = gcsAdapter({
options: {
// you can choose any method for authentication, and authorization which is being provided by `@google-cloud/storage`
keyFilename: './gcs-credentials.json',
//OR
credentials: JSON.parse(process.env.GCS_CREDENTIALS || '{}'), // this env variable will have stringify version of your credentials.json file
},
bucket: process.env.GCS_BUCKET,
})
// Now you can pass this adapter to the plugin
export default buildConfig({
serverUrl: 'https://example.com' // Must be set to use plugin
plugins: [
abrVideos({
collections: {
// The collection users upload source videos to
'my-collection-slug': {keepOriginal: true}
}
}),
gcsAdapter({ //IMPORTANT: Cloud storage plugin must come after plugin
options: {
// you can choose any method for authentication, and authorization which is being provided by `@google-cloud/storage`
keyFilename: './gcs-credentials.json',
//OR
credentials: JSON.parse(process.env.GCS_CREDENTIALS || '{}'), // this env variable will have stringify version of your credentials.json file
},
bucket: process.env.GCS_BUCKET,
collections: {
// The collection users upload source videos to
'my-collection-slug': {
// see docs for the adapter you want to use
adapter: adapter,
},
// the output collection created by the plugin
'segments': { // Required name 'segments' unless overridden in segmentsOverride
adapter: adapter,
},
},
})
]
// The rest of your config goes here
})
If not resolutions array is provide, the plugin will use the default resolutions and bit rates. size specifies the pixel size of the short side of the video so the aspect ratio of any input video is maintained.
For Example: if you input a 4k 16:9 video (The standard landscape video aspect ratio), the plugin will change the video's height to and allow the width to proportionally change.
For the 1080 output the resulting video will be 1080p x 1920p, the standard 1080p pixel size.
The principle is followed for square and portrait videos.
Note: The plugin will only output segments for resolutions that are less than or equal to the short side of the video. Meaning no upscaling is conducted. Example: a 1080p video will only produce output segments at resolutions that are specifed less than or eqaul to 1080.
//payload.config.ts
import { buildConfig } from 'payload/config'
import path from 'path'
import { abrVideos } from 'plugin-adaptive-bitrate-videos`'
export default buildConfig({
serverUrl: 'https://example.com' // Must be set to use pluggin
plugins: [
abrVideos({
collections: {
'my-collection-slug':{
keepOriginal: true,
resolutions: [
{
size: 1080 // pixel size
bitrate: 4000 // kilobits per second
},
// ...more custom resolutions
]
}
}
})
]
// The rest of your config goes here
})
The proper way to conditionally enable/disable this plugin is to use the enabled property.
//payload.config.ts
abrVideos({
enabled: process.env.MY_CONDITION === 'true',
collections: {
'media': {keepOrginal: true}
}
}),
If the code is included in any way in your config but conditionally disabled in another fashion, you may run into issues such as Webpack Build Error: Can't Resolve 'fs' and 'stream' or similar because the plugin must be run at all times in order to properly extend the webpack config.
Override anything on the segments collection by sending a Payload Collection Config to the segmentsOverrides property.
// payload.config.ts
abrVideos({
// ...
segmentOverrides: {
slug: 'contact-forms',
access: {
read: () => true,
update: () => false,
},
fields: [
{
name: 'custom-field',
type: 'text',
},
],
},
})
Optionally set the length of the segments the source video will be divided into. Default length is 2 seconds. The property takes in an number representing seconds.
// payload.config.ts
abrVideos({
// ...
segmentLength: 5, //seconds. Default is 2 seconds.
})
This plugin is configurable to work across many different Payload collections. A * denotes that the property is required.
| Option | Type | Description |
|---|---|---|
collections* | Records<string,CollectionOptions> | Object with keys set to the slug of collections you want to enable the plugin for, and values set to collection-specific options. |
enabled | boolean | Conditionally enable/disable plugin. Default: true. |
segmentsOverrides | PayloadCollectionConfig | Object that overrides the default collection used to store reference to the output segments. Default: SegmentOverrideDefault |
queueName | string | The name of queue used in payload-jobs Default: 'process-abr-videos-queue' |
maxJobs | number | Max number of video processing jobs to allow to be run at once. Default: 1 |
taskOverride | TaskConfig | ((options: TaskConfigurationOptions) => TaskConfig | Promise<TaskConfig>) | Override the default video processing task to run on different compute resources. See taskOverride documentation |
Collection-specific options:
| Option | Type | Description |
|---|---|---|
keepOriginal* | boolean | Conditionally set to keep the original source file after processing. |
resolutions | Array<Resolution> | Set custom resolutions for the plugin to output segment videos to. Default: ResolutionsDefault |
segmentLength | number | Set the output segment length in seconds for each resolution output. Default: 2 |
const SegmentOverrideDefault = {
slug: 'segments',
labels: { singular: 'ABR Segment', plural: 'ABR Segments' },
access: {
read: () => true,
update: () => false,
},
upload: true,
fields: [],
}
const DefaultResolutions = [
{ size: 144, bitrate: 150 },
{ size: 240, bitrate: 250 },
{ size: 360, bitrate: 500 },
{ size: 480, bitrate: 1000 },
{ size: 720, bitrate: 1500 },
{ size: 1080, bitrate: 4000 },
{ size: 1440, bitrate: 6000 },
{ size: 2160, bitrate: 10000 },
]
Any video player that can play .m3u8 files can be used. Here is a simple example using the react-hls-video-player. View docs for react-hls-video-player here.
import React, { useRef, useState } from 'react'
import ReactHlsPlayer from 'react-hls-video-player'
const SimpleHlsPlayer = () => {
const playerRef = useRef(null)
const [isPlaying, setIsPlaying] = useState(false)
const handlePlay = () => {
setIsPlaying(true)
}
const handlePause = () => {
setIsPlaying(false)
}
return (
<div className="w-full max-w-2xl mx-auto">
<ReactHlsPlayer
id="videoElement"
playerRef={playerRef}
src="https://example.com/path/to/your/manifest.m3u8"
className="w-full aspect-video"
autoPlay={false}
controls={true}
onPlay={handlePlay}
onPause={handlePause}
muted
playsInline
/>
<div className="mt-4 text-center">
<p>Player status: {isPlaying ? 'Playing' : 'Paused'}</p>
</div>
</div>
)
}
export default SimpleHlsPlayer
This repository includes a Payload 3 development app in dev/ for both manual testing and automated integration testing.
Install dependencies
pnpm install
Configure environment variables
Copy the example env file and fill in any values you need:
cp dev/.env.example dev/.env
The default values in .env.example work out of the box with the Docker MongoDB setup below. The key variables are:
| Variable | Description | Default |
|---|---|---|
DATABASE_URI | MongoDB connection string | mongodb://127.0.0.1:27017/payload-plugin-template |
PAYLOAD_SECRET | Secret key for Payload CMS | (set your own) |
PAYLOAD_PUBLIC_SERVER_URL | Public URL of the dev server | http://localhost:3000 |
Start MongoDB
A docker-compose.yml is included at the root of the repo. Start MongoDB with:
docker compose up -d
This runs MongoDB 7 on localhost:27017. Data is persisted in a Docker volume between restarts. To stop it:
docker compose down
Start the local Payload/Next app:
pnpm dev
Then open the Payload admin at:
http://localhost:3000/admin
To manually verify the plugin, upload a video from dev/mocks/ into one of the configured upload collections (media or videos). The plugin should queue a processing job, create HLS segment files and playlist manifests in the configured segments collection, and create the master .m3u8 manifest back in the source collection.
Run the integration test suite from the CLI:
NODE_ENV=test pnpm test:int
The integration test setup starts its own MongoDB memory replica set and starts the real Payload/Next dev server automatically. You do not need to run pnpm dev before running the tests.
The tests verify the real upload and processing flow:
.m3u8 manifest is created in the original source collectionBefore publishing, build the package and run the integration suite:
pnpm build
NODE_ENV=test pnpm test:int
To run the this plugin, you will need to run your Payload server on a machine that can comfortably store 2x the max video upload size.
This is required because the source video needs to be temporally stored and the output segments need to be temporally stored before being saved in your final destination.
See Payload Documentation on setting upload limits here.
Also when deployed to production, videos will process slower than processing locally. If the processVideo function is being run in a cloud function environment ensure cloud function timeouts give enough time for videos to process.
For CPU-intensive video processing, you may want to offload the work to dedicated compute resources. The taskOverride option allows you to customize or replace the default video processing task.
Default Task Input Schema:
baseURL (text): Base URL for file accessinputPath (text): Path to input video filekeepOriginal (checkbox): Whether to keep original fileoriginalID (text): ID of original video recordoriginalData (json): Original video metadataresolutions (array): Target resolutions with size and bitratesegmentDuration (number): Segment length in secondsinputCollectionSlug (text): Source collection slugoutputCollectionSlug (text): Target collection slugExample: Custom Processing Task
// payload.config.ts
abrVideos({
collections: {
videos: { keepOriginal: true },
},
taskOverride: {
slug: 'custom-video-processing',
handler: async ({ input, req }) => {
// Custom processing logic
const result = await processVideoHandler(req.payload, input)
return { output: result }
},
retries: 2,
},
})
Example: External Service Integration
// payload.config.ts
abrVideos({
collections: {
videos: { keepOriginal: true },
},
taskOverride: (config) => ({
slug: 'external-video-processing',
handler: async ({ input, req }) => {
// Delegate to external processing service
const response = await fetch(`${process.env.VIDEO_PROCESSING_ENDPOINT}/process`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(input),
})
return await response.json()
},
}),
})
Please open an issue on this repo with any questions or issues about using this plugin.
Topics
mux video
Upload and manage Mux videos from the Payload admin with public or signed playback
872.7k/wk83v3
payload video stream
Send video uploads from Payload collections to Cloudflare Stream and track their processing status
420/wk8v3
cloud storage
Official Payload plugin for storing uploads in third-party cloud storage through a pluggable adapter interface
100426.0k/wk45.4kv3
payload plugin media preview
Preview images, video, audio, code, CSV and Office files in the Payload admin list and edit views
7294/wk13v4
payload plugin janitor
Find orphaned files, missing uploads and unreferenced documents, then delete only what you approve
7031/wk5v4
payload media
Search Unsplash, Pexels and Pixabay stock images and videos from Payload upload collections
680/wk8v4