sanity-plugin-mux-input 2.15.0 → 2.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1150 -177
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1151 -178
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/actions/upload.ts +42 -4
- package/src/components/DraggableWatermark.tsx +877 -0
- package/src/components/PlayerActionsMenu.tsx +14 -0
- package/src/components/ResyncMetadata.tsx +152 -73
- package/src/components/TextTracksManager.tsx +11 -55
- package/src/components/UploadConfiguration.tsx +259 -59
- package/src/components/Uploader.tsx +7 -1
- package/src/components/VideoDetails/VideoDetails.tsx +27 -11
- package/src/components/VideoDetails/useVideoDetails.ts +15 -1
- package/src/components/VideoPlayer.tsx +2 -0
- package/src/hooks/useMediaMetadata.ts +3 -0
- package/src/hooks/useResyncAsset.ts +110 -0
- package/src/hooks/useResyncMuxMetadata.ts +33 -0
- package/src/schema.ts +5 -0
- package/src/util/addKeysToMuxData.ts +30 -0
- package/src/util/convertWatermarkToMux.ts +160 -0
- package/src/util/roundPxString.ts +16 -0
- package/src/util/types.ts +43 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import {useToast} from '@sanity/ui'
|
|
2
|
+
import {useCallback, useState} from 'react'
|
|
3
|
+
|
|
4
|
+
import {getAsset} from '../actions/assets'
|
|
5
|
+
import {addKeysToMuxData} from '../util/addKeysToMuxData'
|
|
6
|
+
import type {MuxAsset, VideoAssetDocument} from '../util/types'
|
|
7
|
+
import {useClient} from './useClient'
|
|
8
|
+
|
|
9
|
+
type ResyncAssetState = 'idle' | 'syncing' | 'success' | 'error'
|
|
10
|
+
|
|
11
|
+
interface UseResyncAssetOptions {
|
|
12
|
+
showToast?: boolean
|
|
13
|
+
onSuccess?: (updatedData: MuxAsset) => void
|
|
14
|
+
onError?: (error: unknown) => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface UseResyncAssetReturn {
|
|
18
|
+
resyncState: ResyncAssetState
|
|
19
|
+
resyncError: unknown
|
|
20
|
+
resyncAsset: (asset: VideoAssetDocument) => Promise<MuxAsset | undefined>
|
|
21
|
+
isResyncing: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function useResyncAsset(options?: UseResyncAssetOptions): UseResyncAssetReturn {
|
|
25
|
+
const client = useClient()
|
|
26
|
+
const toast = useToast()
|
|
27
|
+
const [resyncState, setResyncState] = useState<ResyncAssetState>('idle')
|
|
28
|
+
const [resyncError, setResyncError] = useState<unknown>(null)
|
|
29
|
+
|
|
30
|
+
const showToast = options?.showToast ?? false
|
|
31
|
+
|
|
32
|
+
const resyncAsset = useCallback(
|
|
33
|
+
async (asset: VideoAssetDocument) => {
|
|
34
|
+
if (!asset.assetId) {
|
|
35
|
+
if (showToast) {
|
|
36
|
+
toast.push({
|
|
37
|
+
title: 'Cannot resync',
|
|
38
|
+
description: 'Asset has no Mux ID',
|
|
39
|
+
status: 'error',
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
options?.onError?.(new Error('Asset has no Mux ID'))
|
|
43
|
+
return undefined
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!asset._id) {
|
|
47
|
+
if (showToast) {
|
|
48
|
+
toast.push({
|
|
49
|
+
title: 'Cannot resync',
|
|
50
|
+
description: 'Asset has no document ID',
|
|
51
|
+
status: 'error',
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
options?.onError?.(new Error('Asset has no document ID'))
|
|
55
|
+
return undefined
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setResyncState('syncing')
|
|
59
|
+
setResyncError(null)
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const response = await getAsset(client, asset.assetId)
|
|
63
|
+
const muxData = response.data
|
|
64
|
+
const dataWithKeys = addKeysToMuxData(muxData)
|
|
65
|
+
|
|
66
|
+
await client
|
|
67
|
+
.patch(asset._id)
|
|
68
|
+
.set({
|
|
69
|
+
status: muxData.status,
|
|
70
|
+
data: dataWithKeys,
|
|
71
|
+
...(muxData.meta?.title && {filename: muxData.meta.title}),
|
|
72
|
+
})
|
|
73
|
+
.commit({returnDocuments: false})
|
|
74
|
+
|
|
75
|
+
setResyncState('success')
|
|
76
|
+
if (showToast) {
|
|
77
|
+
toast.push({
|
|
78
|
+
title: 'Asset synced',
|
|
79
|
+
description: 'Data has been updated from Mux',
|
|
80
|
+
status: 'success',
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
options?.onSuccess?.(muxData)
|
|
85
|
+
return muxData
|
|
86
|
+
} catch (error) {
|
|
87
|
+
setResyncState('error')
|
|
88
|
+
setResyncError(error)
|
|
89
|
+
console.error('Failed to refresh asset data:', error)
|
|
90
|
+
if (showToast) {
|
|
91
|
+
toast.push({
|
|
92
|
+
title: 'Sync failed',
|
|
93
|
+
description: 'Could not sync asset from Mux',
|
|
94
|
+
status: 'error',
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
options?.onError?.(error)
|
|
98
|
+
return undefined
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
[client, toast, options, showToast]
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
resyncState,
|
|
106
|
+
resyncError,
|
|
107
|
+
resyncAsset,
|
|
108
|
+
isResyncing: resyncState === 'syncing',
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
useDocumentStore,
|
|
7
7
|
} from 'sanity'
|
|
8
8
|
|
|
9
|
+
import {addKeysToMuxData} from '../util/addKeysToMuxData'
|
|
9
10
|
import {isEmptyOrPlaceholderTitle} from '../util/assetTitlePlaceholder'
|
|
10
11
|
import type {MuxAsset, VideoAssetDocument} from '../util/types'
|
|
11
12
|
import {SANITY_API_VERSION} from './useClient'
|
|
@@ -115,6 +116,37 @@ export default function useResyncMuxMetadata() {
|
|
|
115
116
|
}
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
async function syncFullData() {
|
|
120
|
+
if (!matchedAssets) return
|
|
121
|
+
|
|
122
|
+
setResyncState('syncing')
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const tx = client.transaction()
|
|
126
|
+
|
|
127
|
+
matchedAssets.forEach((matched) => {
|
|
128
|
+
if (!matched.muxAsset) return
|
|
129
|
+
|
|
130
|
+
const dataWithKeys = addKeysToMuxData(matched.muxAsset)
|
|
131
|
+
|
|
132
|
+
// Update all fields: filename, status, and full data from Mux
|
|
133
|
+
tx.patch(matched.sanityDoc._id, {
|
|
134
|
+
set: {
|
|
135
|
+
filename: matched.muxTitle || matched.currentTitle || '',
|
|
136
|
+
status: matched.muxAsset.status,
|
|
137
|
+
data: dataWithKeys,
|
|
138
|
+
},
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
await tx.commit({returnDocuments: false})
|
|
143
|
+
setResyncState('done')
|
|
144
|
+
} catch (error) {
|
|
145
|
+
setResyncState('error')
|
|
146
|
+
setResyncError(error)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
118
150
|
return {
|
|
119
151
|
sanityAssetsLoading,
|
|
120
152
|
closeDialog,
|
|
@@ -124,6 +156,7 @@ export default function useResyncMuxMetadata() {
|
|
|
124
156
|
hasSecrets,
|
|
125
157
|
syncAllVideos,
|
|
126
158
|
syncOnlyEmpty,
|
|
159
|
+
syncFullData,
|
|
127
160
|
matchedAssets,
|
|
128
161
|
muxAssets,
|
|
129
162
|
openDialog,
|
package/src/schema.ts
CHANGED
|
@@ -23,6 +23,11 @@ const muxTrack = {
|
|
|
23
23
|
{type: 'number', name: 'max_frame_rate'},
|
|
24
24
|
{type: 'number', name: 'duration'},
|
|
25
25
|
{type: 'number', name: 'max_height'},
|
|
26
|
+
{type: 'string', name: 'language_code'},
|
|
27
|
+
{type: 'string', name: 'name'},
|
|
28
|
+
{type: 'string', name: 'status'},
|
|
29
|
+
{type: 'string', name: 'text_source'},
|
|
30
|
+
{type: 'string', name: 'text_type'},
|
|
26
31
|
],
|
|
27
32
|
}
|
|
28
33
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {uuid} from '@sanity/uuid'
|
|
2
|
+
|
|
3
|
+
import type {MuxAsset} from './types'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Adds _key to array items in MuxAsset data for Sanity compatibility.
|
|
7
|
+
* Sanity requires _key on array items for proper editing support.
|
|
8
|
+
*/
|
|
9
|
+
export function addKeysToMuxData(data: MuxAsset): MuxAsset {
|
|
10
|
+
return {
|
|
11
|
+
...data,
|
|
12
|
+
tracks: data.tracks?.map((track) => ({
|
|
13
|
+
...track,
|
|
14
|
+
_key: uuid(),
|
|
15
|
+
})),
|
|
16
|
+
playback_ids: data.playback_ids?.map((playbackId) => ({
|
|
17
|
+
...playbackId,
|
|
18
|
+
_key: uuid(),
|
|
19
|
+
})),
|
|
20
|
+
static_renditions: data.static_renditions
|
|
21
|
+
? {
|
|
22
|
+
...data.static_renditions,
|
|
23
|
+
files: data.static_renditions.files?.map((file) => ({
|
|
24
|
+
...file,
|
|
25
|
+
_key: uuid(),
|
|
26
|
+
})),
|
|
27
|
+
}
|
|
28
|
+
: undefined,
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import {roundPxString} from './roundPxString'
|
|
2
|
+
import type {MuxOverlaySettings, WatermarkConfig} from './types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Converts a draggable watermark position (x, y percentages) to Mux's overlay_settings format.
|
|
6
|
+
*
|
|
7
|
+
* @param watermark - The watermark configuration with position, size, and opacity
|
|
8
|
+
* @returns Mux overlay_settings object
|
|
9
|
+
* @see {@link https://www.mux.com/docs/guides/add-watermarks-to-your-videos}
|
|
10
|
+
*/
|
|
11
|
+
export function convertWatermarkToMuxOverlay(
|
|
12
|
+
watermark: WatermarkConfig,
|
|
13
|
+
options?: {
|
|
14
|
+
/**
|
|
15
|
+
* Video aspect ratio (width / height). Needed for correct vertical positioning,
|
|
16
|
+
* especially on vertical videos.
|
|
17
|
+
*/
|
|
18
|
+
videoAspectRatio?: number
|
|
19
|
+
/**
|
|
20
|
+
* Unit to emit for margins/width when generating overlay_settings from Canvas mode.
|
|
21
|
+
* - 'px' will generate pixel strings according to Mux's scaling rules:
|
|
22
|
+
* values are applied as if the video were scaled to 1920x1080 (horizontal)
|
|
23
|
+
* or 1080x1920 (vertical).
|
|
24
|
+
* - '%' preserves existing behavior.
|
|
25
|
+
*/
|
|
26
|
+
units?: '%' | 'px'
|
|
27
|
+
}
|
|
28
|
+
): MuxOverlaySettings | null {
|
|
29
|
+
if (!watermark.enabled || !watermark.imageUrl) {
|
|
30
|
+
return null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const size = watermark.size || 20
|
|
34
|
+
const opacity = watermark.opacity ?? 0.7
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Convert a percentage to whole-pixel string, using Mux's base dimensions:
|
|
38
|
+
* - Horizontal video: 1920x1080
|
|
39
|
+
* - Vertical video: 1080x1920
|
|
40
|
+
*/
|
|
41
|
+
const toPxString = (valuePercent: number, axis: 'x' | 'y') => {
|
|
42
|
+
const videoAspectRatio = options?.videoAspectRatio ?? 16 / 9
|
|
43
|
+
const isVertical = videoAspectRatio > 0 && videoAspectRatio < 1
|
|
44
|
+
const baseW = isVertical ? 1080 : 1920
|
|
45
|
+
const baseH = isVertical ? 1920 : 1080
|
|
46
|
+
const base = axis === 'x' ? baseW : baseH
|
|
47
|
+
const px = (valuePercent / 100) * base
|
|
48
|
+
let rounded = Math.round(px)
|
|
49
|
+
// Avoid sending 0px (and JS -0); keep sign for negative margins.
|
|
50
|
+
if (rounded === 0) rounded = px < 0 ? -1 : 1
|
|
51
|
+
return `${rounded}px`
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const normalizeToPixels = (value: string | undefined, axis: 'x' | 'y'): string | undefined => {
|
|
55
|
+
if (!value) return value
|
|
56
|
+
const trimmed = value.trim()
|
|
57
|
+
if (trimmed.endsWith('px')) {
|
|
58
|
+
return roundPxString(trimmed)
|
|
59
|
+
}
|
|
60
|
+
if (trimmed.endsWith('%')) {
|
|
61
|
+
const n = Number(trimmed.slice(0, -1))
|
|
62
|
+
if (!Number.isFinite(n)) return value
|
|
63
|
+
return toPxString(n, axis)
|
|
64
|
+
}
|
|
65
|
+
return value
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// If user provided explicit overlay settings, use them (Mux-documented format).
|
|
69
|
+
// When `options.units === 'px'`, we normalize both % and px to whole-pixel strings,
|
|
70
|
+
// honoring vertical vs horizontal video bases.
|
|
71
|
+
if (watermark.overlay_settings) {
|
|
72
|
+
const widthValue = watermark.overlay_settings.width
|
|
73
|
+
const widthNormalized =
|
|
74
|
+
options?.units === 'px' ? normalizeToPixels(widthValue, 'x') : widthValue
|
|
75
|
+
return {
|
|
76
|
+
...watermark.overlay_settings,
|
|
77
|
+
horizontal_margin:
|
|
78
|
+
options?.units === 'px'
|
|
79
|
+
? (normalizeToPixels(watermark.overlay_settings.horizontal_margin, 'x') ??
|
|
80
|
+
watermark.overlay_settings.horizontal_margin)
|
|
81
|
+
: watermark.overlay_settings.horizontal_margin,
|
|
82
|
+
vertical_margin:
|
|
83
|
+
options?.units === 'px'
|
|
84
|
+
? (normalizeToPixels(watermark.overlay_settings.vertical_margin, 'y') ??
|
|
85
|
+
watermark.overlay_settings.vertical_margin)
|
|
86
|
+
: watermark.overlay_settings.vertical_margin,
|
|
87
|
+
width: widthNormalized ?? `${size}%`,
|
|
88
|
+
opacity: watermark.overlay_settings.opacity ?? `${Math.round(opacity * 100)}%`,
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const position = watermark.position || {x: 50, y: 50}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Our UI stores watermark position as the *center point* in percentages.
|
|
96
|
+
* Mux margins are interpreted relative to an *edge* (based on align).
|
|
97
|
+
*
|
|
98
|
+
* To make "corner" placements match what the user dragged, we convert from
|
|
99
|
+
* center-position to top-left margins by subtracting half the watermark size.
|
|
100
|
+
*
|
|
101
|
+
* Note: `size` is a percentage of video width. Mux `width` is also expressed
|
|
102
|
+
* as a percentage of the video width, so we can reuse it for horizontal math.
|
|
103
|
+
* For vertical math, we approximate using the same percentage to keep behavior
|
|
104
|
+
* consistent with the current draggable UI (which also uses `size` in both axes
|
|
105
|
+
* for bounds).
|
|
106
|
+
*/
|
|
107
|
+
// Allow negative margins to compensate for rounding / letterboxing edge-cases.
|
|
108
|
+
// We still clamp to a sane range so values don't explode.
|
|
109
|
+
const clampPercent = (value: number) => Math.max(-100, Math.min(100, value))
|
|
110
|
+
|
|
111
|
+
// Mux accepts percentage strings; avoid sending an exact "0%" by nudging to 0.01%.
|
|
112
|
+
// This also handles the JS -0 edge case and tiny floating point remnants.
|
|
113
|
+
const toPercentString = (value: number) => {
|
|
114
|
+
const epsilon = 1e-9
|
|
115
|
+
const isZeroish = value === 0 || Object.is(value, -0) || Math.abs(value) < epsilon
|
|
116
|
+
return `${isZeroish ? 0.01 : value}%`
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const watermarkWidthPercentOfVideoWidth = size
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Convert watermark height into % of video height.
|
|
123
|
+
* height% = (watermarkWidthPx / imageAspectRatio) / videoHeightPx
|
|
124
|
+
* = (size% * videoWidthPx / imageAspectRatio) / videoHeightPx
|
|
125
|
+
* = size% * (videoWidthPx/videoHeightPx) / imageAspectRatio
|
|
126
|
+
* = size% * videoAspectRatio / imageAspectRatio
|
|
127
|
+
*/
|
|
128
|
+
const videoAspectRatio = options?.videoAspectRatio ?? 16 / 9
|
|
129
|
+
const imageAspectRatio = watermark.imageAspectRatio ?? 1
|
|
130
|
+
const watermarkHeightPercentOfVideoHeight = Math.max(
|
|
131
|
+
0,
|
|
132
|
+
Math.min(100, (size * videoAspectRatio) / imageAspectRatio)
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
const halfWidth = watermarkWidthPercentOfVideoWidth / 2
|
|
136
|
+
const halfHeight = watermarkHeightPercentOfVideoHeight / 2
|
|
137
|
+
|
|
138
|
+
const leftMargin = clampPercent(
|
|
139
|
+
Math.min(position.x - halfWidth, 100 - watermarkWidthPercentOfVideoWidth)
|
|
140
|
+
)
|
|
141
|
+
const topMargin = clampPercent(
|
|
142
|
+
Math.min(position.y - halfHeight, 100 - watermarkHeightPercentOfVideoHeight)
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
const units = options?.units ?? '%'
|
|
146
|
+
const marginX = units === 'px' ? toPxString(leftMargin, 'x') : toPercentString(leftMargin)
|
|
147
|
+
const marginY = units === 'px' ? toPxString(topMargin, 'y') : toPercentString(topMargin)
|
|
148
|
+
const width = units === 'px' ? toPxString(size, 'x') : `${size}%`
|
|
149
|
+
|
|
150
|
+
const overlaySettings: MuxOverlaySettings = {
|
|
151
|
+
vertical_align: 'top',
|
|
152
|
+
vertical_margin: marginY,
|
|
153
|
+
horizontal_align: 'left',
|
|
154
|
+
horizontal_margin: marginX,
|
|
155
|
+
width,
|
|
156
|
+
opacity: `${Math.round(opacity * 100)}%`,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return overlaySettings
|
|
160
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rounds the numeric part of a px string to the nearest integer.
|
|
3
|
+
* Returns undefined if the value is not a valid px string or not a finite number.
|
|
4
|
+
* Avoids sending 0px (and JS -0); snaps to ±1 instead.
|
|
5
|
+
*/
|
|
6
|
+
export function roundPxString(value: unknown): string | undefined {
|
|
7
|
+
if (typeof value !== 'string') return undefined
|
|
8
|
+
const trimmed = value.trim()
|
|
9
|
+
if (!trimmed.endsWith('px')) return undefined
|
|
10
|
+
const n = Number(trimmed.slice(0, -2))
|
|
11
|
+
if (!Number.isFinite(n)) return undefined
|
|
12
|
+
let rounded = Math.round(n)
|
|
13
|
+
// Avoid sending 0px (and JS -0); keep sign when negative.
|
|
14
|
+
if (rounded === 0) rounded = n < 0 ? -1 : 1
|
|
15
|
+
return `${rounded}px`
|
|
16
|
+
}
|
package/src/util/types.ts
CHANGED
|
@@ -274,12 +274,52 @@ export function isAutogeneratedTrack(
|
|
|
274
274
|
|
|
275
275
|
export type UploadTextTrack = AutogeneratedTextTrack | CustomTextTrack
|
|
276
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Watermark configuration for UI (draggable position)
|
|
279
|
+
*/
|
|
280
|
+
export interface WatermarkConfig {
|
|
281
|
+
enabled: boolean
|
|
282
|
+
imageUrl?: string
|
|
283
|
+
/**
|
|
284
|
+
* Aspect ratio (width / height) of the watermark image.
|
|
285
|
+
* Used to convert between our center-based UI coords and Mux overlay margins.
|
|
286
|
+
*/
|
|
287
|
+
imageAspectRatio?: number
|
|
288
|
+
/**
|
|
289
|
+
* Optional explicit Mux `overlay_settings`.
|
|
290
|
+
* When set, these values should be used as-is for the upload request and preview.
|
|
291
|
+
* @see {@link https://www.mux.com/docs/guides/add-watermarks-to-your-videos}
|
|
292
|
+
*/
|
|
293
|
+
overlay_settings?: MuxOverlaySettings
|
|
294
|
+
position?: {
|
|
295
|
+
x: number // percentage (0-100)
|
|
296
|
+
y: number // percentage (0-100)
|
|
297
|
+
}
|
|
298
|
+
size?: number // percentage of video width (0-100)
|
|
299
|
+
opacity?: number // 0-1 (converted to percentage string for Mux API)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Mux overlay_settings format for watermark API
|
|
304
|
+
* @see {@link https://www.mux.com/docs/guides/add-watermarks-to-your-videos}
|
|
305
|
+
*/
|
|
306
|
+
export interface MuxOverlaySettings {
|
|
307
|
+
vertical_align: 'top' | 'middle' | 'bottom'
|
|
308
|
+
vertical_margin: string // percentage (e.g., "10%") or pixels (e.g., "40px")
|
|
309
|
+
horizontal_align: 'left' | 'center' | 'right'
|
|
310
|
+
horizontal_margin: string // percentage (e.g., "10%") or pixels (e.g., "40px")
|
|
311
|
+
width?: string // percentage (e.g., "25%") or pixels (e.g., "80px")
|
|
312
|
+
height?: string // percentage or pixels
|
|
313
|
+
opacity?: string // percentage string (e.g., "90%")
|
|
314
|
+
}
|
|
315
|
+
|
|
277
316
|
export interface UploadConfig
|
|
278
317
|
extends Pick<MuxInputConfig, 'max_resolution_tier' | 'normalize_audio' | 'video_quality'> {
|
|
279
318
|
static_renditions: StaticRenditionResolution[]
|
|
280
319
|
text_tracks: UploadTextTrack[]
|
|
281
320
|
signed_policy: boolean
|
|
282
321
|
public_policy: boolean
|
|
322
|
+
watermark?: WatermarkConfig
|
|
283
323
|
drm_policy: boolean
|
|
284
324
|
}
|
|
285
325
|
|
|
@@ -309,7 +349,7 @@ export interface MuxNewAssetSettings
|
|
|
309
349
|
/** The time offset in seconds from the beginning of the video indicating the clip's ending marker. */
|
|
310
350
|
end_time?: number
|
|
311
351
|
/** This parameter is required for text type tracks. */
|
|
312
|
-
type
|
|
352
|
+
type?: 'video' | 'audio' | 'text'
|
|
313
353
|
/** Type of text track. This parameter only supports subtitles value. */
|
|
314
354
|
text_type?: 'subtitles'
|
|
315
355
|
/** The language code value must be a valid BCP 47 specification compliant value. */
|
|
@@ -320,6 +360,8 @@ export interface MuxNewAssetSettings
|
|
|
320
360
|
closed_captions?: boolean
|
|
321
361
|
/** This optional parameter should be used tracks with type of text and text_type set to subtitles. */
|
|
322
362
|
passthrough?: string
|
|
363
|
+
/** Overlay settings for watermarks. Used when adding a watermark image as a second input. */
|
|
364
|
+
overlay_settings?: MuxOverlaySettings
|
|
323
365
|
}[]
|
|
324
366
|
|
|
325
367
|
/** An array of playback policy names that you want applied to this asset and available through playback_ids. */
|