Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 49 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,21 +198,65 @@ sanity documents delete secrets.mux

More information on signed URLs is available on Mux's [docs](https://docs.mux.com/docs/headless-cms-sanity#advanced-signed-urls)

### MP4 support (downloadable videos or offline viewing)
### Static Renditions (downloadable videos or offline viewing)

To enable [static MP4 renditions](https://docs.mux.com/guides/video/enable-static-mp4-renditions), add `mp4_support: 'standard'` to the `options` of your `mux.video` schema type.
To enable [static MP4 renditions](https://docs.mux.com/guides/video/enable-static-mp4-renditions), add `static_renditions` to your plugin configuration. This allows users to download videos for offline viewing.

#### Standard Mode (Recommended)

```js
import {muxInput} from 'sanity-plugin-mux-input'

export default defineConfig({
plugins: [muxInput({mp4_support: 'standard'})],
plugins: [
muxInput({
static_renditions: ['highest'], // Enables MP4 downloads at the highest quality (up to 4K)
// or
static_renditions: ['highest', 'audio-only'], // Also includes audio-only (M4A) downloads
}),
],
})
```

If MP4 support is enabled in the plugin's configuration, editors can still choose to enable MP4 renditions on a per-video basis when uploading new assets.
**Standard mode options:**
- `'highest'`: Produces an MP4 file with video resolution up to 4K (2160p)
- `'audio-only'`: Produces an M4A (audio-only MP4) file

#### Advanced Mode (Specific Resolutions)

For more control, you can specify exact resolutions:

```js
import {muxInput} from 'sanity-plugin-mux-input'

export default defineConfig({
plugins: [
muxInput({
static_renditions: ['1080p', '720p', 'audio-only'],
}),
],
})
```

**Advanced mode options:**
- Specific resolutions: `'270p'`, `'360p'`, `'480p'`, `'540p'`, `'720p'`, `'1080p'`, `'1440p'`, `'2160p'`
- `'audio-only'`: M4A file

**Important notes:**
- You cannot mix `'highest'` with specific resolutions (e.g., `['highest', '1080p']` is invalid)
- Mux will not upscale videos - renditions requiring upscaling are automatically skipped
- When uploading new assets, editors can choose different rendition settings on a per-video basis

#### Backward Compatibility

The deprecated `mp4_support` field is still supported for backward compatibility:

```js
// ⚠️ Deprecated - use static_renditions instead
muxInput({mp4_support: 'standard'}) // Equivalent to static_renditions: ['highest']
```

MP4 allows users to download videos for later or offline viewing. More information can be found on Mux's [documentation](https://docs.mux.com/guides/enable-static-mp4-renditions).
More information can be found on Mux's [documentation](https://docs.mux.com/guides/enable-static-mp4-renditions).

### Video resolution (max_resolution_tier)

Expand Down
2 changes: 1 addition & 1 deletion sanity.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default defineConfig({
muxInput({
video_quality: 'plus',
max_resolution_tier: '2160p',
mp4_support: 'standard',
static_renditions: ['highest'],
defaultAutogeneratedSubtitleLang: 'en',
}),
visionTool(),
Expand Down
28 changes: 26 additions & 2 deletions src/_exports/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import createStudioTool, {DEFAULT_TOOL_CONFIG} from '../components/StudioTool'
import {muxVideoCustomRendering} from '../plugin'
import {muxVideoSchema, schemaTypes} from '../schema'
import type {PluginConfig} from '../util/types'
import type {PluginConfig, StaticRenditionResolution} from '../util/types'
export type {VideoAssetDocument} from '../util/types'

export const defaultConfig: PluginConfig = {
static_renditions: [],
mp4_support: 'none',
video_quality: 'plus',
max_resolution_tier: '1080p',
Expand All @@ -17,8 +18,27 @@
allowedRolesForConfiguration: [],
}

/**
* Converts legacy mp4_support configuration to static_renditions format
*/
function convertLegacyConfig(config: Partial<PluginConfig>): {
static_renditions: StaticRenditionResolution[]
} {
// If static_renditions is already provided, use it
if (config.static_renditions && config.static_renditions.length > 0) {
return {static_renditions: config.static_renditions}
}

// Convert legacy mp4_support to static_renditions
if (config.mp4_support === 'standard') {
return {static_renditions: ['highest']}
}

return {static_renditions: []}
}

export const muxInput = definePlugin<Partial<PluginConfig> | void>((userConfig) => {
// TODO: Remove this on next major version when we end support for encoding_tier

Check warning on line 41 in src/_exports/index.ts

View workflow job for this annotation

GitHub Actions / Lint & Build

Unexpected 'todo' comment: 'TODO: Remove this on next major version...'

Check warning on line 41 in src/_exports/index.ts

View workflow job for this annotation

GitHub Actions / Node.js lts/* / ubuntu-latest

Unexpected 'todo' comment: 'TODO: Remove this on next major version...'

Check warning on line 41 in src/_exports/index.ts

View workflow job for this annotation

GitHub Actions / Node.js lts/-2 / ubuntu-latest

Unexpected 'todo' comment: 'TODO: Remove this on next major version...'

Check warning on line 41 in src/_exports/index.ts

View workflow job for this annotation

GitHub Actions / Node.js current / ubuntu-latest

Unexpected 'todo' comment: 'TODO: Remove this on next major version...'

Check warning on line 41 in src/_exports/index.ts

View workflow job for this annotation

GitHub Actions / Node.js lts/* / macos-latest

Unexpected 'todo' comment: 'TODO: Remove this on next major version...'

Check warning on line 41 in src/_exports/index.ts

View workflow job for this annotation

GitHub Actions / Node.js lts/* / windows-latest

Unexpected 'todo' comment: 'TODO: Remove this on next major version...'
if (typeof userConfig === 'object' && 'encoding_tier' in userConfig) {
const deprecated_encoding_tier = userConfig.encoding_tier
if (!userConfig.video_quality) {
Expand All @@ -30,7 +50,11 @@
}
}
}
const config: PluginConfig = {...defaultConfig, ...(userConfig || {})}
const config: PluginConfig = {
...defaultConfig,
...(userConfig || {}),
...convertLegacyConfig(userConfig || {}),
}
return {
name: 'mux-input',
schema: {
Expand Down
2 changes: 1 addition & 1 deletion src/actions/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ type UploadResponse = {
cors_origin: string
id: string
new_asset_settings: {
mp4_support: 'standard' | 'none'
static_renditions?: {resolution: string}[]
passthrough: string
playback_policies: ['public' | 'signed']
}
Expand Down
19 changes: 14 additions & 5 deletions src/components/Player.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,23 @@ const Player = ({asset, buttons, readOnly, onChange}: Props) => {
return true
}, [asset])
const isPreparingStaticRenditions = useMemo<boolean>(() => {
if (asset?.data?.static_renditions?.status === 'preparing') {
return true
// Legacy: If static_renditions has a status field, it was created with mp4_support (deprecated)
// We don't process this old format, just return false
// Note: 'disabled' status is valid in the new format when no renditions were requested
if (
asset?.data?.static_renditions?.status &&
asset?.data?.static_renditions?.status !== 'disabled'
) {
return false
}
if (asset?.data?.static_renditions?.status === 'ready') {

// Check if any file in static_renditions is still preparing
const files = asset?.data?.static_renditions?.files
if (!files || files.length === 0) {
return false
}
return false
}, [asset?.data?.static_renditions?.status])
return files.some((file) => file.status === 'preparing')
}, [asset?.data?.static_renditions?.status, asset?.data?.static_renditions?.files])
const playRef = useRef<HTMLDivElement>(null)
const muteRef = useRef<HTMLDivElement>(null)
const handleCancelUpload = useCancelUpload(asset, onChange)
Expand Down
Loading
Loading