sanity-plugin-mux-input 3.0.5 → 4.0.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 +28 -28
- package/dist/index.js.map +1 -1
- package/package.json +5 -15
- package/dist/index.cjs +0 -5746
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -288
- package/dist/index.d.cts.map +0 -1
- package/sanity.json +0 -8
- package/src/_exports/index.ts +0 -73
- package/src/actions/assets.ts +0 -152
- package/src/actions/secrets.ts +0 -110
- package/src/actions/upload.ts +0 -308
- package/src/clients/upChunkObservable.ts +0 -54
- package/src/components/AddCaptionDialog.tsx +0 -440
- package/src/components/CaptionsDialog.tsx +0 -23
- package/src/components/ConfigureApi.styled.tsx +0 -19
- package/src/components/ConfigureApi.tsx +0 -296
- package/src/components/DraggableWatermark.tsx +0 -885
- package/src/components/EditCaptionDialog.tsx +0 -511
- package/src/components/EditThumbnailDialog.tsx +0 -121
- package/src/components/ErrorBoundaryCard.tsx +0 -97
- package/src/components/FileInputButton.tsx +0 -54
- package/src/components/FileInputMenuItem.styled.tsx +0 -36
- package/src/components/FileInputMenuItem.tsx +0 -85
- package/src/components/FormField.tsx +0 -38
- package/src/components/IconInfo.tsx +0 -22
- package/src/components/ImportVideosFromMux.tsx +0 -339
- package/src/components/Input.styled.tsx +0 -22
- package/src/components/Input.tsx +0 -78
- package/src/components/InputBrowser.tsx +0 -41
- package/src/components/MuxLogo.tsx +0 -42
- package/src/components/Onboard.tsx +0 -65
- package/src/components/PageSelector.tsx +0 -54
- package/src/components/Player.styled.tsx +0 -11
- package/src/components/Player.tsx +0 -117
- package/src/components/PlayerActionsMenu.tsx +0 -191
- package/src/components/ResyncMetadata.tsx +0 -278
- package/src/components/SelectAsset.tsx +0 -39
- package/src/components/SelectSortOptions.tsx +0 -45
- package/src/components/SpinnerBox.tsx +0 -16
- package/src/components/StudioTool.tsx +0 -24
- package/src/components/TextTracksEditor.tsx +0 -117
- package/src/components/TextTracksManager.tsx +0 -738
- package/src/components/UploadConfiguration.tsx +0 -696
- package/src/components/UploadPlaceholder.tsx +0 -88
- package/src/components/UploadProgress.tsx +0 -80
- package/src/components/Uploader.styled.tsx +0 -65
- package/src/components/Uploader.tsx +0 -499
- package/src/components/VideoDetails/DeleteDialog.tsx +0 -148
- package/src/components/VideoDetails/VideoDetails.tsx +0 -358
- package/src/components/VideoDetails/VideoReferences.tsx +0 -63
- package/src/components/VideoDetails/useVideoDetails.ts +0 -103
- package/src/components/VideoInBrowser.tsx +0 -245
- package/src/components/VideoMetadata.tsx +0 -45
- package/src/components/VideoPlayer.tsx +0 -241
- package/src/components/VideoThumbnail.tsx +0 -139
- package/src/components/VideosBrowser.tsx +0 -100
- package/src/components/documentPreview/DocumentPreview.tsx +0 -84
- package/src/components/documentPreview/DraftStatus.tsx +0 -34
- package/src/components/documentPreview/MissingSchemaType.tsx +0 -32
- package/src/components/documentPreview/PaneItemPreview.tsx +0 -67
- package/src/components/documentPreview/PublishedStatus.tsx +0 -35
- package/src/components/documentPreview/TimeAgo.tsx +0 -12
- package/src/components/icons/Audio.tsx +0 -13
- package/src/components/icons/Resolution.tsx +0 -12
- package/src/components/icons/StopWatch.tsx +0 -20
- package/src/components/icons/ToolIcon.tsx +0 -19
- package/src/components/uploadConfiguration/PlaybackPolicy.tsx +0 -133
- package/src/components/uploadConfiguration/PlaybackPolicyOption.tsx +0 -76
- package/src/components/uploadConfiguration/PlaybackPolicyWarning.tsx +0 -29
- package/src/components/uploadConfiguration/ResolutionTierSelector.tsx +0 -72
- package/src/components/uploadConfiguration/StaticRenditionSelector.tsx +0 -180
- package/src/components/withFocusRing/helpers.ts +0 -24
- package/src/components/withFocusRing/index.ts +0 -1
- package/src/components/withFocusRing/withFocusRing.ts +0 -30
- package/src/context/DialogStateContext.tsx +0 -33
- package/src/context/DrmPlaybackWarningContext.tsx +0 -97
- package/src/hooks/useAccessControl.ts +0 -13
- package/src/hooks/useAssetDocumentValues.ts +0 -11
- package/src/hooks/useAssets.ts +0 -73
- package/src/hooks/useCancelUpload.ts +0 -22
- package/src/hooks/useClient.ts +0 -8
- package/src/hooks/useDialogState.ts +0 -11
- package/src/hooks/useDocReferences.ts +0 -21
- package/src/hooks/useFetchFileSize.ts +0 -55
- package/src/hooks/useImportMuxAssets.ts +0 -132
- package/src/hooks/useInView.ts +0 -41
- package/src/hooks/useMediaMetadata.ts +0 -104
- package/src/hooks/useMuxAssets.ts +0 -179
- package/src/hooks/useMuxPolling.ts +0 -52
- package/src/hooks/useResyncAsset.ts +0 -110
- package/src/hooks/useResyncMuxMetadata.ts +0 -169
- package/src/hooks/useSaveSecrets.ts +0 -78
- package/src/hooks/useSecretsDocumentValues.ts +0 -38
- package/src/hooks/useSecretsFormState.ts +0 -47
- package/src/plugin.tsx +0 -31
- package/src/sanity-ui.d.ts +0 -5
- package/src/schema.ts +0 -196
- package/src/util/addKeysToMuxData.ts +0 -30
- package/src/util/asserters.ts +0 -23
- package/src/util/assetTitlePlaceholder.ts +0 -31
- package/src/util/constants.ts +0 -15
- package/src/util/convertWatermarkToMux.ts +0 -160
- package/src/util/createSearchFilter.ts +0 -76
- package/src/util/createUrlParamsObject.ts +0 -29
- package/src/util/extractFiles.ts +0 -67
- package/src/util/formatBytes.ts +0 -31
- package/src/util/formatDriveShareLink.ts +0 -64
- package/src/util/formatSeconds.ts +0 -48
- package/src/util/generateJwt.ts +0 -57
- package/src/util/getAnimatedPosterSrc.ts +0 -26
- package/src/util/getPlaybackPolicy.ts +0 -69
- package/src/util/getPosterSrc.ts +0 -28
- package/src/util/getVideoMetadata.ts +0 -23
- package/src/util/getVideoSrc.ts +0 -23
- package/src/util/parsers.ts +0 -5
- package/src/util/pluginVersion.ts +0 -5
- package/src/util/readSecrets.ts +0 -38
- package/src/util/roundPxString.ts +0 -16
- package/src/util/textTracks.ts +0 -222
- package/src/util/tryWithSuspend.ts +0 -22
- package/src/util/types.ts +0 -566
- package/v2-incompatible.js +0 -11
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import {Flex, Radio, Text} from '@sanity/ui'
|
|
2
|
-
import {type ActionDispatch} from 'react'
|
|
3
|
-
import {FormField} from 'sanity'
|
|
4
|
-
|
|
5
|
-
import {type UploadConfig} from '../../util/types'
|
|
6
|
-
import {type UploadConfigurationStateAction} from '../UploadConfiguration'
|
|
7
|
-
|
|
8
|
-
export const RESOLUTION_TIERS = [
|
|
9
|
-
{value: '1080p', label: '1080p'},
|
|
10
|
-
{value: '1440p', label: '1440p (2k)'},
|
|
11
|
-
{value: '2160p', label: '2160p (4k)'},
|
|
12
|
-
] as const satisfies {value: UploadConfig['max_resolution_tier']; label: string}[]
|
|
13
|
-
|
|
14
|
-
export const ResolutionTierSelector = ({
|
|
15
|
-
id,
|
|
16
|
-
config,
|
|
17
|
-
dispatch,
|
|
18
|
-
maxSupportedResolution,
|
|
19
|
-
}: {
|
|
20
|
-
id: string
|
|
21
|
-
config: UploadConfig
|
|
22
|
-
dispatch: ActionDispatch<[action: UploadConfigurationStateAction]>
|
|
23
|
-
maxSupportedResolution: number
|
|
24
|
-
}) => {
|
|
25
|
-
return (
|
|
26
|
-
<FormField
|
|
27
|
-
path={[]}
|
|
28
|
-
title="Resolution Tier"
|
|
29
|
-
description={
|
|
30
|
-
<>
|
|
31
|
-
The maximum{' '}
|
|
32
|
-
<a
|
|
33
|
-
href="https://docs.mux.com/api-reference#video/operation/create-direct-upload"
|
|
34
|
-
target="_blank"
|
|
35
|
-
rel="noopener noreferrer"
|
|
36
|
-
>
|
|
37
|
-
resolution_tier
|
|
38
|
-
</a>{' '}
|
|
39
|
-
your asset is encoded, stored, and streamed at.
|
|
40
|
-
</>
|
|
41
|
-
}
|
|
42
|
-
>
|
|
43
|
-
<Flex gap={3} wrap={'wrap'}>
|
|
44
|
-
{RESOLUTION_TIERS.map(({value, label}, index) => {
|
|
45
|
-
const inputId = `${id}--type-${value}`
|
|
46
|
-
|
|
47
|
-
if (index > maxSupportedResolution) return null
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<Flex key={value} align="center" gap={2}>
|
|
51
|
-
<Radio
|
|
52
|
-
checked={config.max_resolution_tier === value}
|
|
53
|
-
name="asset-resolutiontier"
|
|
54
|
-
onChange={(e) =>
|
|
55
|
-
dispatch({
|
|
56
|
-
action: 'max_resolution_tier',
|
|
57
|
-
value: e.currentTarget.value as UploadConfig['max_resolution_tier'],
|
|
58
|
-
})
|
|
59
|
-
}
|
|
60
|
-
value={value}
|
|
61
|
-
id={inputId}
|
|
62
|
-
/>
|
|
63
|
-
<Text as="label" htmlFor={inputId}>
|
|
64
|
-
{label}
|
|
65
|
-
</Text>
|
|
66
|
-
</Flex>
|
|
67
|
-
)
|
|
68
|
-
})}
|
|
69
|
-
</Flex>
|
|
70
|
-
</FormField>
|
|
71
|
-
)
|
|
72
|
-
}
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import {Checkbox, Flex, Label, Radio, Stack, Text} from '@sanity/ui'
|
|
2
|
-
import {type ActionDispatch, useMemo, useState} from 'react'
|
|
3
|
-
import {FormField} from 'sanity'
|
|
4
|
-
|
|
5
|
-
import {type StaticRenditionResolution, type UploadConfig} from '../../util/types'
|
|
6
|
-
import {type UploadConfigurationStateAction} from '../UploadConfiguration'
|
|
7
|
-
|
|
8
|
-
const ADVANCED_RESOLUTIONS: {value: StaticRenditionResolution; label: string}[] = [
|
|
9
|
-
{value: '270p', label: '270p'},
|
|
10
|
-
{value: '360p', label: '360p'},
|
|
11
|
-
{value: '480p', label: '480p'},
|
|
12
|
-
{value: '540p', label: '540p'},
|
|
13
|
-
{value: '720p', label: '720p'},
|
|
14
|
-
{value: '1080p', label: '1080p'},
|
|
15
|
-
{value: '1440p', label: '1440p'},
|
|
16
|
-
{value: '2160p', label: '2160p'},
|
|
17
|
-
]
|
|
18
|
-
|
|
19
|
-
export const StaticRenditionSelector = ({
|
|
20
|
-
id,
|
|
21
|
-
config,
|
|
22
|
-
dispatch,
|
|
23
|
-
}: {
|
|
24
|
-
id: string
|
|
25
|
-
config: UploadConfig
|
|
26
|
-
dispatch: ActionDispatch<[action: UploadConfigurationStateAction]>
|
|
27
|
-
}) => {
|
|
28
|
-
// Determine if user is in advanced mode based on selected renditions
|
|
29
|
-
const isAdvancedMode = useMemo(() => {
|
|
30
|
-
const specificResolutions = config.static_renditions.filter(
|
|
31
|
-
(r) => r !== 'highest' && r !== 'audio-only',
|
|
32
|
-
)
|
|
33
|
-
return specificResolutions.length > 0
|
|
34
|
-
}, [config.static_renditions])
|
|
35
|
-
|
|
36
|
-
const [renditionMode, setRenditionMode] = useState<'standard' | 'advanced'>(
|
|
37
|
-
isAdvancedMode ? 'advanced' : 'standard',
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
// Helper to toggle a rendition
|
|
41
|
-
const toggleRendition = (rendition: StaticRenditionResolution) => {
|
|
42
|
-
const current = config.static_renditions
|
|
43
|
-
const hasRendition = current.includes(rendition)
|
|
44
|
-
|
|
45
|
-
if (hasRendition) {
|
|
46
|
-
dispatch({
|
|
47
|
-
action: 'static_renditions',
|
|
48
|
-
value: current.filter((r) => r !== rendition),
|
|
49
|
-
})
|
|
50
|
-
} else {
|
|
51
|
-
dispatch({
|
|
52
|
-
action: 'static_renditions',
|
|
53
|
-
value: [...current, rendition],
|
|
54
|
-
})
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// When switching modes, clear renditions that don't apply
|
|
59
|
-
const handleModeChange = (mode: 'standard' | 'advanced') => {
|
|
60
|
-
setRenditionMode(mode)
|
|
61
|
-
if (mode === 'standard') {
|
|
62
|
-
// Remove specific resolutions, keep only highest and audio-only
|
|
63
|
-
dispatch({
|
|
64
|
-
action: 'static_renditions',
|
|
65
|
-
value: config.static_renditions.filter((r) => r === 'highest' || r === 'audio-only'),
|
|
66
|
-
})
|
|
67
|
-
} else {
|
|
68
|
-
// Remove highest, keep specific resolutions and audio-only
|
|
69
|
-
dispatch({
|
|
70
|
-
action: 'static_renditions',
|
|
71
|
-
value: config.static_renditions.filter((r) => r !== 'highest'),
|
|
72
|
-
})
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
return (
|
|
76
|
-
<Stack space={3}>
|
|
77
|
-
<FormField
|
|
78
|
-
path={[]}
|
|
79
|
-
title="Static Renditions"
|
|
80
|
-
description="Generate downloadable MP4 or M4A files. Note: Mux will not upscale to produce MP4 renditions - renditions that would cause upscaling are skipped."
|
|
81
|
-
>
|
|
82
|
-
<Stack space={3}>
|
|
83
|
-
{/* Mode Selector */}
|
|
84
|
-
<Flex gap={3}>
|
|
85
|
-
<Flex align="center" gap={2}>
|
|
86
|
-
<Radio
|
|
87
|
-
checked={renditionMode === 'standard'}
|
|
88
|
-
name="rendition-mode"
|
|
89
|
-
onChange={() => handleModeChange('standard')}
|
|
90
|
-
value="standard"
|
|
91
|
-
id={`${id}--mode-standard`}
|
|
92
|
-
/>
|
|
93
|
-
<Text as="label" htmlFor={`${id}--mode-standard`}>
|
|
94
|
-
Standard
|
|
95
|
-
</Text>
|
|
96
|
-
</Flex>
|
|
97
|
-
<Flex align="center" gap={2}>
|
|
98
|
-
<Radio
|
|
99
|
-
checked={renditionMode === 'advanced'}
|
|
100
|
-
name="rendition-mode"
|
|
101
|
-
onChange={() => handleModeChange('advanced')}
|
|
102
|
-
value="advanced"
|
|
103
|
-
id={`${id}--mode-advanced`}
|
|
104
|
-
/>
|
|
105
|
-
<Text as="label" htmlFor={`${id}--mode-advanced`}>
|
|
106
|
-
Advanced
|
|
107
|
-
</Text>
|
|
108
|
-
</Flex>
|
|
109
|
-
</Flex>
|
|
110
|
-
|
|
111
|
-
{/* Standard Mode Options */}
|
|
112
|
-
{renditionMode === 'standard' && (
|
|
113
|
-
<Stack space={2}>
|
|
114
|
-
<Flex align="center" gap={2} padding={[0, 2]}>
|
|
115
|
-
<Checkbox
|
|
116
|
-
id={`${id}--highest`}
|
|
117
|
-
style={{display: 'block'}}
|
|
118
|
-
checked={config.static_renditions.includes('highest')}
|
|
119
|
-
onChange={() => toggleRendition('highest')}
|
|
120
|
-
/>
|
|
121
|
-
<Text as="label" htmlFor={`${id}--highest`}>
|
|
122
|
-
Highest Resolution (up to 4K)
|
|
123
|
-
</Text>
|
|
124
|
-
</Flex>
|
|
125
|
-
<Flex align="center" gap={2} padding={[0, 2]}>
|
|
126
|
-
<Checkbox
|
|
127
|
-
id={`${id}--audio-only-standard`}
|
|
128
|
-
style={{display: 'block'}}
|
|
129
|
-
checked={config.static_renditions.includes('audio-only')}
|
|
130
|
-
onChange={() => toggleRendition('audio-only')}
|
|
131
|
-
/>
|
|
132
|
-
<Text as="label" htmlFor={`${id}--audio-only-standard`}>
|
|
133
|
-
Audio Only (M4A)
|
|
134
|
-
</Text>
|
|
135
|
-
</Flex>
|
|
136
|
-
</Stack>
|
|
137
|
-
)}
|
|
138
|
-
|
|
139
|
-
{/* Advanced Mode Options */}
|
|
140
|
-
{renditionMode === 'advanced' && (
|
|
141
|
-
<Stack space={2}>
|
|
142
|
-
<Label size={1} muted>
|
|
143
|
-
Select specific resolutions:
|
|
144
|
-
</Label>
|
|
145
|
-
<Flex gap={2} wrap="wrap">
|
|
146
|
-
{ADVANCED_RESOLUTIONS.map(({value, label}) => {
|
|
147
|
-
const inputId = `${id}--resolution-${value}`
|
|
148
|
-
return (
|
|
149
|
-
<Flex key={value} align="center" gap={2}>
|
|
150
|
-
<Checkbox
|
|
151
|
-
id={inputId}
|
|
152
|
-
style={{display: 'block'}}
|
|
153
|
-
checked={config.static_renditions.includes(value)}
|
|
154
|
-
onChange={() => toggleRendition(value)}
|
|
155
|
-
/>
|
|
156
|
-
<Text as="label" htmlFor={inputId} size={1}>
|
|
157
|
-
{label}
|
|
158
|
-
</Text>
|
|
159
|
-
</Flex>
|
|
160
|
-
)
|
|
161
|
-
})}
|
|
162
|
-
</Flex>
|
|
163
|
-
<Flex align="center" gap={2} padding={[2, 2, 0, 2]}>
|
|
164
|
-
<Checkbox
|
|
165
|
-
id={`${id}--audio-only-advanced`}
|
|
166
|
-
style={{display: 'block'}}
|
|
167
|
-
checked={config.static_renditions.includes('audio-only')}
|
|
168
|
-
onChange={() => toggleRendition('audio-only')}
|
|
169
|
-
/>
|
|
170
|
-
<Text as="label" htmlFor={`${id}--audio-only-advanced`}>
|
|
171
|
-
Audio Only (M4A)
|
|
172
|
-
</Text>
|
|
173
|
-
</Flex>
|
|
174
|
-
</Stack>
|
|
175
|
-
)}
|
|
176
|
-
</Stack>
|
|
177
|
-
</FormField>
|
|
178
|
-
</Stack>
|
|
179
|
-
)
|
|
180
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// todo: get these utils from @sanity/ui instead
|
|
2
|
-
export function focusRingBorderStyle(border: {color: string; width: number}): string {
|
|
3
|
-
return `inset 0 0 0 ${border.width}px ${border.color}`
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export function focusRingStyle(opts: {
|
|
7
|
-
base?: {bg: string}
|
|
8
|
-
border?: {color: string; width: number}
|
|
9
|
-
focusRing: {offset: number; width: number}
|
|
10
|
-
}): string {
|
|
11
|
-
const {base, border, focusRing} = opts
|
|
12
|
-
const focusRingOutsetWidth = focusRing.offset + focusRing.width
|
|
13
|
-
const focusRingInsetWidth = 0 - focusRing.offset
|
|
14
|
-
const bgColor = base ? base.bg : 'var(--card-bg-color)'
|
|
15
|
-
|
|
16
|
-
return [
|
|
17
|
-
focusRingInsetWidth > 0 && `inset 0 0 0 ${focusRingInsetWidth}px var(--card-focus-ring-color)`,
|
|
18
|
-
border && focusRingBorderStyle(border),
|
|
19
|
-
focusRingInsetWidth < 0 && `0 0 0 ${0 - focusRingInsetWidth}px ${bgColor}`,
|
|
20
|
-
focusRingOutsetWidth > 0 && `0 0 0 ${focusRingOutsetWidth}px var(--card-focus-ring-color)`,
|
|
21
|
-
]
|
|
22
|
-
.filter(Boolean)
|
|
23
|
-
.join(',')
|
|
24
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './withFocusRing'
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import {rem} from '@sanity/ui'
|
|
2
|
-
import type {ComponentType} from 'react'
|
|
3
|
-
import {css, styled} from 'styled-components'
|
|
4
|
-
|
|
5
|
-
import {focusRingBorderStyle, focusRingStyle} from './helpers'
|
|
6
|
-
|
|
7
|
-
export function withFocusRing<Props>(component: ComponentType<Props>) {
|
|
8
|
-
return styled(component as unknown as any)<Props & {$border?: boolean}>((props) => {
|
|
9
|
-
const border = {
|
|
10
|
-
width: props.$border ? 1 : 0,
|
|
11
|
-
color: 'var(--card-border-color)',
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
return css`
|
|
15
|
-
--card-focus-box-shadow: ${focusRingBorderStyle(border)};
|
|
16
|
-
|
|
17
|
-
border-radius: ${rem(props.theme.sanity.radius[1]!)};
|
|
18
|
-
outline: none;
|
|
19
|
-
box-shadow: var(--card-focus-box-shadow);
|
|
20
|
-
|
|
21
|
-
&:focus {
|
|
22
|
-
--card-focus-box-shadow: ${focusRingStyle({
|
|
23
|
-
base: props.theme.sanity.color.base,
|
|
24
|
-
border,
|
|
25
|
-
focusRing: props.theme.sanity.focusRing,
|
|
26
|
-
})};
|
|
27
|
-
}
|
|
28
|
-
`
|
|
29
|
-
})
|
|
30
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import React, {createContext, useContext, useMemo} from 'react'
|
|
2
|
-
|
|
3
|
-
import {type DialogState, type SetDialogState} from '../hooks/useDialogState'
|
|
4
|
-
|
|
5
|
-
type DialogStateContextProps = {
|
|
6
|
-
dialogState: DialogState
|
|
7
|
-
setDialogState: SetDialogState
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const DialogStateContext = createContext<DialogStateContextProps>({
|
|
11
|
-
dialogState: false,
|
|
12
|
-
setDialogState: () => {
|
|
13
|
-
return null
|
|
14
|
-
},
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
interface DialogStateProviderProps extends DialogStateContextProps {
|
|
18
|
-
children: React.ReactNode
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const DialogStateProvider = ({
|
|
22
|
-
dialogState,
|
|
23
|
-
setDialogState,
|
|
24
|
-
children,
|
|
25
|
-
}: DialogStateProviderProps) => {
|
|
26
|
-
const value = useMemo(() => ({dialogState, setDialogState}), [dialogState, setDialogState])
|
|
27
|
-
return <DialogStateContext.Provider value={value}>{children}</DialogStateContext.Provider>
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export const useDialogStateContext = () => {
|
|
31
|
-
const context = useContext(DialogStateContext)
|
|
32
|
-
return context
|
|
33
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import {Button, Card, Dialog, Stack, Text} from '@sanity/ui'
|
|
2
|
-
import React, {createContext, useCallback, useContext, useMemo, useState} from 'react'
|
|
3
|
-
|
|
4
|
-
import {type PluginConfig} from '../util/types'
|
|
5
|
-
|
|
6
|
-
const LOCAL_STORAGE_HAS_SHOWN_WARNING_KEY = 'mux-plugin-has-shown-drm-playback-warning'
|
|
7
|
-
|
|
8
|
-
type DrmPlaybackWarningContextContextProps = {
|
|
9
|
-
hasShownWarning: boolean
|
|
10
|
-
setHasWarnedAboutDrmPlayback: (b: boolean) => void
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const DrmPlaybackWarningContext = createContext<DrmPlaybackWarningContextContextProps>({
|
|
14
|
-
hasShownWarning: false,
|
|
15
|
-
setHasWarnedAboutDrmPlayback: () => {
|
|
16
|
-
return null
|
|
17
|
-
},
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
interface DrmPlaybackWarningContextProviderProps {
|
|
21
|
-
config?: PluginConfig
|
|
22
|
-
children: React.ReactNode
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const DrmPlaybackWarningContextProvider = ({
|
|
26
|
-
config,
|
|
27
|
-
children,
|
|
28
|
-
}: DrmPlaybackWarningContextProviderProps) => {
|
|
29
|
-
const warningDisabled = config?.disableDrmPlaybackWarning ?? false
|
|
30
|
-
const hasWarned: boolean =
|
|
31
|
-
warningDisabled || window.localStorage.getItem(LOCAL_STORAGE_HAS_SHOWN_WARNING_KEY) === 'true'
|
|
32
|
-
const [hasWarnedAboutDrmPlayback, setHasWarnedAboutDrmPlayback] = useState(hasWarned)
|
|
33
|
-
|
|
34
|
-
const setHasShownWarning = useCallback((b: boolean) => {
|
|
35
|
-
window.localStorage.setItem(LOCAL_STORAGE_HAS_SHOWN_WARNING_KEY, b.toString())
|
|
36
|
-
setHasWarnedAboutDrmPlayback(b)
|
|
37
|
-
}, [])
|
|
38
|
-
|
|
39
|
-
const value = useMemo(
|
|
40
|
-
() => ({
|
|
41
|
-
hasShownWarning: hasWarnedAboutDrmPlayback,
|
|
42
|
-
setHasWarnedAboutDrmPlayback: setHasShownWarning,
|
|
43
|
-
}),
|
|
44
|
-
[hasWarnedAboutDrmPlayback, setHasShownWarning],
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<DrmPlaybackWarningContext.Provider value={value}>
|
|
49
|
-
{children}
|
|
50
|
-
</DrmPlaybackWarningContext.Provider>
|
|
51
|
-
)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export const useDrmPlaybackWarningContext = () => {
|
|
55
|
-
const context = useContext(DrmPlaybackWarningContext)
|
|
56
|
-
return context
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export const DRMWarningDialog = ({onClose}: {onClose: () => void}) => {
|
|
60
|
-
const {setHasWarnedAboutDrmPlayback} = useDrmPlaybackWarningContext()
|
|
61
|
-
const _onClose = () => {
|
|
62
|
-
setHasWarnedAboutDrmPlayback(true)
|
|
63
|
-
onClose()
|
|
64
|
-
}
|
|
65
|
-
return (
|
|
66
|
-
<Dialog
|
|
67
|
-
open
|
|
68
|
-
id="drm-playback-warn"
|
|
69
|
-
onClose={_onClose}
|
|
70
|
-
header="DRM Playback Warning"
|
|
71
|
-
footer={
|
|
72
|
-
<Stack padding={3}>
|
|
73
|
-
<Button mode="ghost" tone="primary" onClick={_onClose} text="Ok" />
|
|
74
|
-
</Stack>
|
|
75
|
-
}
|
|
76
|
-
>
|
|
77
|
-
<Stack space={3} padding={3}>
|
|
78
|
-
<Card padding={[3, 3, 3]} radius={2}>
|
|
79
|
-
<Stack space={3}>
|
|
80
|
-
<Text size={1} weight="semibold">
|
|
81
|
-
DRM-protected playback will generate a license with a small associated cost. The
|
|
82
|
-
plugin will attempt to play signed or public playback IDs instead whenever possible.
|
|
83
|
-
</Text>
|
|
84
|
-
</Stack>
|
|
85
|
-
</Card>
|
|
86
|
-
<Card padding={[3, 3, 3]} radius={2} tone="suggest">
|
|
87
|
-
<Stack space={3}>
|
|
88
|
-
<Text size={1} weight="semibold">
|
|
89
|
-
This is a one time warning. If it persists, you can disable it from your plugin
|
|
90
|
-
configuration.
|
|
91
|
-
</Text>
|
|
92
|
-
</Stack>
|
|
93
|
-
</Card>
|
|
94
|
-
</Stack>
|
|
95
|
-
</Dialog>
|
|
96
|
-
)
|
|
97
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import {useCurrentUser} from 'sanity'
|
|
2
|
-
|
|
3
|
-
import {type PluginConfig} from '../util/types'
|
|
4
|
-
|
|
5
|
-
export const useAccessControl = (config: PluginConfig) => {
|
|
6
|
-
const user = useCurrentUser()
|
|
7
|
-
|
|
8
|
-
const hasConfigAccess =
|
|
9
|
-
!config?.allowedRolesForConfiguration?.length ||
|
|
10
|
-
user?.roles?.some((role) => config.allowedRolesForConfiguration.includes(role.name))
|
|
11
|
-
|
|
12
|
-
return {hasConfigAccess}
|
|
13
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import {isReference} from 'sanity'
|
|
2
|
-
import {useDocumentValues} from 'sanity'
|
|
3
|
-
|
|
4
|
-
import type {Reference, VideoAssetDocument} from '../util/types'
|
|
5
|
-
|
|
6
|
-
const path = ['assetId', 'data', 'playbackId', 'status', 'thumbTime', 'filename']
|
|
7
|
-
export const useAssetDocumentValues = (asset: Reference | null | undefined) =>
|
|
8
|
-
useDocumentValues<VideoAssetDocument | null | undefined>(
|
|
9
|
-
isReference(asset) ? asset._ref : '',
|
|
10
|
-
path,
|
|
11
|
-
)
|
package/src/hooks/useAssets.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import {useMemo, useState} from 'react'
|
|
2
|
-
import {
|
|
3
|
-
collate,
|
|
4
|
-
createHookFromObservableFactory,
|
|
5
|
-
type DocumentStore,
|
|
6
|
-
useDocumentStore,
|
|
7
|
-
} from 'sanity'
|
|
8
|
-
|
|
9
|
-
import {SANITY_API_VERSION} from '../hooks/useClient'
|
|
10
|
-
import {createSearchFilter} from '../util/createSearchFilter'
|
|
11
|
-
import type {VideoAssetDocument} from '../util/types'
|
|
12
|
-
|
|
13
|
-
export const ASSET_SORT_OPTIONS = {
|
|
14
|
-
createdDesc: {groq: '_createdAt desc', label: 'Newest first'},
|
|
15
|
-
createdAsc: {groq: '_createdAt asc', label: 'First created (oldest)'},
|
|
16
|
-
filenameAsc: {groq: 'filename asc', label: 'By filename (A-Z)'},
|
|
17
|
-
filenameDesc: {groq: 'filename desc', label: 'By filename (Z-A)'},
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export type SortOption = keyof typeof ASSET_SORT_OPTIONS
|
|
21
|
-
|
|
22
|
-
const useAssetDocuments = createHookFromObservableFactory<
|
|
23
|
-
VideoAssetDocument[],
|
|
24
|
-
{
|
|
25
|
-
documentStore: DocumentStore
|
|
26
|
-
sort: SortOption
|
|
27
|
-
searchQuery: string
|
|
28
|
-
}
|
|
29
|
-
>(({documentStore, sort, searchQuery}) => {
|
|
30
|
-
const search = createSearchFilter(searchQuery)
|
|
31
|
-
const filter = [`_type == "mux.videoAsset"`, ...search.filter].filter(Boolean).join(' && ')
|
|
32
|
-
|
|
33
|
-
const sortFragment = ASSET_SORT_OPTIONS[sort].groq
|
|
34
|
-
return documentStore.listenQuery(
|
|
35
|
-
/* groq */ `*[${filter}] | order(${sortFragment})`,
|
|
36
|
-
search.params,
|
|
37
|
-
{
|
|
38
|
-
apiVersion: SANITY_API_VERSION,
|
|
39
|
-
},
|
|
40
|
-
)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
export default function useAssets() {
|
|
44
|
-
const documentStore = useDocumentStore()
|
|
45
|
-
const [sort, setSort] = useState<SortOption>('createdDesc')
|
|
46
|
-
const [searchQuery, setSearchQuery] = useState('')
|
|
47
|
-
|
|
48
|
-
const [assetDocuments = [], isLoading] = useAssetDocuments(
|
|
49
|
-
useMemo(() => ({documentStore, sort, searchQuery}), [documentStore, sort, searchQuery]),
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
const assets = useMemo(
|
|
53
|
-
() =>
|
|
54
|
-
// Avoid displaying both drafts & published assets by collating them together and giving preference to drafts
|
|
55
|
-
collate<VideoAssetDocument>(assetDocuments).map(
|
|
56
|
-
(collated) =>
|
|
57
|
-
({
|
|
58
|
-
...(collated.draft || collated.published),
|
|
59
|
-
_id: collated.id,
|
|
60
|
-
}) as VideoAssetDocument,
|
|
61
|
-
),
|
|
62
|
-
[assetDocuments],
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
assets,
|
|
67
|
-
isLoading,
|
|
68
|
-
sort,
|
|
69
|
-
searchQuery,
|
|
70
|
-
setSort,
|
|
71
|
-
setSearchQuery,
|
|
72
|
-
}
|
|
73
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import {useCallback} from 'react'
|
|
2
|
-
import {PatchEvent, unset} from 'sanity'
|
|
3
|
-
|
|
4
|
-
import {deleteAssetOnMux} from '../actions/assets'
|
|
5
|
-
import {useClient} from '../hooks/useClient'
|
|
6
|
-
import type {MuxInputProps, VideoAssetDocument} from '../util/types'
|
|
7
|
-
|
|
8
|
-
export const useCancelUpload = (asset: VideoAssetDocument, onChange: MuxInputProps['onChange']) => {
|
|
9
|
-
const client = useClient()
|
|
10
|
-
return useCallback(() => {
|
|
11
|
-
if (!asset) {
|
|
12
|
-
return
|
|
13
|
-
}
|
|
14
|
-
onChange(PatchEvent.from(unset()))
|
|
15
|
-
if (asset.assetId) {
|
|
16
|
-
void deleteAssetOnMux(client, asset.assetId)
|
|
17
|
-
}
|
|
18
|
-
if (asset._id) {
|
|
19
|
-
void client.delete(asset._id)
|
|
20
|
-
}
|
|
21
|
-
}, [asset, client, onChange])
|
|
22
|
-
}
|
package/src/hooks/useClient.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
// As it's required to specify the API Version this custom hook ensures it's all using the same version
|
|
2
|
-
import {useClient as useSanityClient} from 'sanity'
|
|
3
|
-
|
|
4
|
-
export const SANITY_API_VERSION = '2024-03-05'
|
|
5
|
-
|
|
6
|
-
export function useClient() {
|
|
7
|
-
return useSanityClient({apiVersion: SANITY_API_VERSION})
|
|
8
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// Handy little state machine to simplify managing which root level dialog to open
|
|
2
|
-
|
|
3
|
-
import {useState} from 'react'
|
|
4
|
-
|
|
5
|
-
export type DialogState = 'secrets' | 'select-video' | 'edit-thumbnail' | 'edit-captions' | false
|
|
6
|
-
|
|
7
|
-
export function useDialogState() {
|
|
8
|
-
return useState<DialogState>(false)
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export type SetDialogState = ReturnType<typeof useDialogState>[1]
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import {createHookFromObservableFactory, type DocumentStore, type SanityDocument} from 'sanity'
|
|
2
|
-
|
|
3
|
-
import {SANITY_API_VERSION} from './useClient'
|
|
4
|
-
|
|
5
|
-
const useDocReferences = createHookFromObservableFactory<
|
|
6
|
-
SanityDocument[],
|
|
7
|
-
{
|
|
8
|
-
documentStore: DocumentStore
|
|
9
|
-
id: string
|
|
10
|
-
}
|
|
11
|
-
>(({documentStore, id}) => {
|
|
12
|
-
return documentStore.listenQuery(
|
|
13
|
-
/* groq */ '*[references($id)]{_id, _type, _rev, _updatedAt, _createdAt}',
|
|
14
|
-
{id},
|
|
15
|
-
{
|
|
16
|
-
apiVersion: SANITY_API_VERSION,
|
|
17
|
-
},
|
|
18
|
-
)
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
export default useDocReferences
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import {useEffect, useState} from 'react'
|
|
2
|
-
|
|
3
|
-
import {type StagedUpload} from '../components/Uploader'
|
|
4
|
-
|
|
5
|
-
export function useFetchFileSize(stagedUpload: StagedUpload, maxFileSize?: number) {
|
|
6
|
-
const [fileSize, setFileSize] = useState<number | null>(null)
|
|
7
|
-
const [isLoadingFileSize, setIsLoadingFileSize] = useState(false)
|
|
8
|
-
const [canSkipFileSizeValidation, setCanSkipFileSizeValidation] = useState(false)
|
|
9
|
-
|
|
10
|
-
useEffect(() => {
|
|
11
|
-
// Fetch URL Upload file size
|
|
12
|
-
if (stagedUpload.type === 'url') {
|
|
13
|
-
// oxlint-disable-next-line react/react-compiler
|
|
14
|
-
setIsLoadingFileSize(false)
|
|
15
|
-
setCanSkipFileSizeValidation(false)
|
|
16
|
-
setFileSize(null)
|
|
17
|
-
const url = stagedUpload.url
|
|
18
|
-
|
|
19
|
-
// Get file size from URL
|
|
20
|
-
const fetchFileSize = async () => {
|
|
21
|
-
setIsLoadingFileSize(true)
|
|
22
|
-
try {
|
|
23
|
-
const response = await fetch(url, {method: 'HEAD'})
|
|
24
|
-
const contentLength = response.headers.get('content-length')
|
|
25
|
-
const newFileSize = contentLength ? parseInt(contentLength, 10) : null
|
|
26
|
-
|
|
27
|
-
setIsLoadingFileSize(false)
|
|
28
|
-
if (newFileSize) {
|
|
29
|
-
setFileSize(newFileSize)
|
|
30
|
-
}
|
|
31
|
-
if (newFileSize === null && maxFileSize !== undefined) {
|
|
32
|
-
// Size unknown but size limit is configured - skip file size validation
|
|
33
|
-
setCanSkipFileSizeValidation(true)
|
|
34
|
-
}
|
|
35
|
-
} catch {
|
|
36
|
-
console.warn('Could not validate file size from URL')
|
|
37
|
-
// Skip validation of file size, but still validate duration
|
|
38
|
-
setCanSkipFileSizeValidation(true)
|
|
39
|
-
setIsLoadingFileSize(false)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
void fetchFileSize()
|
|
44
|
-
}
|
|
45
|
-
if (stagedUpload.type === 'file') {
|
|
46
|
-
setFileSize(stagedUpload.files[0]!.size)
|
|
47
|
-
}
|
|
48
|
-
}, [maxFileSize, stagedUpload, stagedUpload.type])
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
fileSize,
|
|
52
|
-
isLoadingFileSize,
|
|
53
|
-
canSkipFileSizeValidation,
|
|
54
|
-
}
|
|
55
|
-
}
|