sanity-plugin-mux-input 3.0.5 → 4.0.1
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 +20 -92
- 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
package/src/components/Input.tsx
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import {Card} from '@sanity/ui'
|
|
2
|
-
import {memo, Suspense} from 'react'
|
|
3
|
-
|
|
4
|
-
import {useAccessControl} from '../hooks/useAccessControl'
|
|
5
|
-
import {useAssetDocumentValues} from '../hooks/useAssetDocumentValues'
|
|
6
|
-
import {useClient} from '../hooks/useClient'
|
|
7
|
-
import {useDialogState} from '../hooks/useDialogState'
|
|
8
|
-
import {useMuxPolling} from '../hooks/useMuxPolling'
|
|
9
|
-
import {useSecretsDocumentValues} from '../hooks/useSecretsDocumentValues'
|
|
10
|
-
import type {MuxInputProps, PluginConfig} from '../util/types'
|
|
11
|
-
import {ConfigureApiDialog} from './ConfigureApi'
|
|
12
|
-
import ErrorBoundaryCard from './ErrorBoundaryCard'
|
|
13
|
-
import {InputFallback} from './Input.styled'
|
|
14
|
-
import Onboard from './Onboard'
|
|
15
|
-
import Uploader from './Uploader'
|
|
16
|
-
|
|
17
|
-
export interface InputProps extends MuxInputProps {
|
|
18
|
-
config: PluginConfig
|
|
19
|
-
}
|
|
20
|
-
const Input = (props: InputProps) => {
|
|
21
|
-
const client = useClient()
|
|
22
|
-
const secretDocumentValues = useSecretsDocumentValues()
|
|
23
|
-
const assetDocumentValues = useAssetDocumentValues(props.value?.asset)
|
|
24
|
-
const poll = useMuxPolling(props.readOnly ? undefined : assetDocumentValues?.value || undefined)
|
|
25
|
-
const [dialogState, setDialogState] = useDialogState()
|
|
26
|
-
const {hasConfigAccess} = useAccessControl(props.config)
|
|
27
|
-
|
|
28
|
-
const error = secretDocumentValues.error || assetDocumentValues.error || poll.error /*||
|
|
29
|
-
// @TODO move errored logic to Uploader, where handleRemoveVideo can be called
|
|
30
|
-
(assetDocumentValues.value?.status === 'errored'
|
|
31
|
-
? new Error(assetDocumentValues.value.data?.errors?.messages?.join(' '))
|
|
32
|
-
: undefined)
|
|
33
|
-
// */
|
|
34
|
-
if (error) {
|
|
35
|
-
// @TODO deal with it more gracefully
|
|
36
|
-
throw error
|
|
37
|
-
}
|
|
38
|
-
const isLoading = secretDocumentValues.isLoading || assetDocumentValues.isLoading
|
|
39
|
-
|
|
40
|
-
return (
|
|
41
|
-
<Card>
|
|
42
|
-
<ErrorBoundaryCard schemaType={props.schemaType}>
|
|
43
|
-
<Suspense fallback={<InputFallback />}>
|
|
44
|
-
{isLoading ? (
|
|
45
|
-
<InputFallback />
|
|
46
|
-
) : (
|
|
47
|
-
<>
|
|
48
|
-
{secretDocumentValues.value.needsSetup && !assetDocumentValues.value ? (
|
|
49
|
-
<Onboard setDialogState={setDialogState} config={props.config} />
|
|
50
|
-
) : (
|
|
51
|
-
<Uploader
|
|
52
|
-
{...props}
|
|
53
|
-
config={props.config}
|
|
54
|
-
onChange={props.onChange}
|
|
55
|
-
client={client}
|
|
56
|
-
secrets={secretDocumentValues.value.secrets}
|
|
57
|
-
asset={assetDocumentValues.value}
|
|
58
|
-
dialogState={dialogState}
|
|
59
|
-
setDialogState={setDialogState}
|
|
60
|
-
needsSetup={secretDocumentValues.value.needsSetup}
|
|
61
|
-
/>
|
|
62
|
-
)}
|
|
63
|
-
|
|
64
|
-
{dialogState === 'secrets' && hasConfigAccess && (
|
|
65
|
-
<ConfigureApiDialog
|
|
66
|
-
setDialogState={setDialogState}
|
|
67
|
-
secrets={secretDocumentValues.value.secrets}
|
|
68
|
-
/>
|
|
69
|
-
)}
|
|
70
|
-
</>
|
|
71
|
-
)}
|
|
72
|
-
</Suspense>
|
|
73
|
-
</ErrorBoundaryCard>
|
|
74
|
-
</Card>
|
|
75
|
-
)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export default memo(Input)
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import {Dialog} from '@sanity/ui'
|
|
2
|
-
import {useCallback, useId} from 'react'
|
|
3
|
-
import {styled} from 'styled-components'
|
|
4
|
-
|
|
5
|
-
import type {SetDialogState} from '../hooks/useDialogState'
|
|
6
|
-
import SelectAsset, {type Props as SelectAssetProps} from './SelectAsset'
|
|
7
|
-
|
|
8
|
-
/** To prevent Content Layout Shift (CLS), ensure that the dialog always occupies the entire available height. */
|
|
9
|
-
const StyledDialog = styled(Dialog)`
|
|
10
|
-
> div[data-ui='DialogCard'] > div[data-ui='Card'] {
|
|
11
|
-
height: 100%;
|
|
12
|
-
}
|
|
13
|
-
`
|
|
14
|
-
|
|
15
|
-
export default function InputBrowser({
|
|
16
|
-
setDialogState,
|
|
17
|
-
asset,
|
|
18
|
-
onChange,
|
|
19
|
-
config,
|
|
20
|
-
}: Pick<SelectAssetProps, 'onChange' | 'asset' | 'config'> & {
|
|
21
|
-
setDialogState: SetDialogState
|
|
22
|
-
}) {
|
|
23
|
-
const id = `InputBrowser${useId()}`
|
|
24
|
-
const handleClose = useCallback(() => setDialogState(false), [setDialogState])
|
|
25
|
-
return (
|
|
26
|
-
<StyledDialog
|
|
27
|
-
__unstable_autoFocus
|
|
28
|
-
header="Select video"
|
|
29
|
-
id={id}
|
|
30
|
-
onClose={handleClose}
|
|
31
|
-
width={2}
|
|
32
|
-
>
|
|
33
|
-
<SelectAsset
|
|
34
|
-
config={config}
|
|
35
|
-
asset={asset}
|
|
36
|
-
onChange={onChange}
|
|
37
|
-
setDialogState={setDialogState}
|
|
38
|
-
/>
|
|
39
|
-
</StyledDialog>
|
|
40
|
-
)
|
|
41
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import {useTheme_v2} from '@sanity/ui'
|
|
2
|
-
import {type CSSProperties, useId, useMemo} from 'react'
|
|
3
|
-
|
|
4
|
-
export interface Props {
|
|
5
|
-
height?: number
|
|
6
|
-
}
|
|
7
|
-
export default function MuxLogo({height = 26}: Props) {
|
|
8
|
-
const id = useId()
|
|
9
|
-
const theme = useTheme_v2()
|
|
10
|
-
const fillColor = theme.color._dark ? 'white' : 'black'
|
|
11
|
-
const titleId = useMemo(() => `${id}-title`, [id])
|
|
12
|
-
|
|
13
|
-
const pathStyle: CSSProperties = {
|
|
14
|
-
fillRule: 'nonzero',
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<svg
|
|
19
|
-
aria-labelledby={titleId}
|
|
20
|
-
style={{height: `${height}px`}}
|
|
21
|
-
viewBox="0 0 1600 500"
|
|
22
|
-
version="1.1"
|
|
23
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
24
|
-
xmlSpace="preserve"
|
|
25
|
-
>
|
|
26
|
-
<g id="Layer-1" fill={fillColor}>
|
|
27
|
-
<path
|
|
28
|
-
d="M994.287,93.486c-17.121,-0 -31,-13.879 -31,-31c0,-17.121 13.879,-31 31,-31c17.121,-0 31,13.879 31,31c0,17.121 -13.879,31 -31,31m0,-93.486c-34.509,-0 -62.484,27.976 -62.484,62.486l0,187.511c0,68.943 -56.09,125.033 -125.032,125.033c-68.942,-0 -125.03,-56.09 -125.03,-125.033l0,-187.511c0,-34.51 -27.976,-62.486 -62.485,-62.486c-34.509,-0 -62.484,27.976 -62.484,62.486l0,187.511c0,137.853 112.149,250.003 249.999,250.003c137.851,-0 250.001,-112.15 250.001,-250.003l0,-187.511c0,-34.51 -27.976,-62.486 -62.485,-62.486"
|
|
29
|
-
style={pathStyle}
|
|
30
|
-
/>
|
|
31
|
-
<path
|
|
32
|
-
d="M1537.51,468.511c-17.121,-0 -31,-13.879 -31,-31c0,-17.121 13.879,-31 31,-31c17.121,-0 31,13.879 31,31c0,17.121 -13.879,31 -31,31m-275.883,-218.509l-143.33,143.329c-24.402,24.402 -24.402,63.966 0,88.368c24.402,24.402 63.967,24.402 88.369,-0l143.33,-143.329l143.328,143.329c24.402,24.4 63.967,24.402 88.369,-0c24.403,-24.402 24.403,-63.966 0.001,-88.368l-143.33,-143.329l0.001,-0.004l143.329,-143.329c24.402,-24.402 24.402,-63.965 0,-88.367c-24.402,-24.402 -63.967,-24.402 -88.369,-0l-143.329,143.328l-143.329,-143.328c-24.402,-24.401 -63.967,-24.402 -88.369,-0c-24.402,24.402 -24.402,63.965 0,88.367l143.329,143.329l0,0.004Z"
|
|
33
|
-
style={pathStyle}
|
|
34
|
-
/>
|
|
35
|
-
<path
|
|
36
|
-
d="M437.511,468.521c-17.121,-0 -31,-13.879 -31,-31c0,-17.121 13.879,-31 31,-31c17.121,-0 31,13.879 31,31c0,17.121 -13.879,31 -31,31m23.915,-463.762c-23.348,-9.672 -50.226,-4.327 -68.096,13.544l-143.331,143.329l-143.33,-143.329c-17.871,-17.871 -44.747,-23.216 -68.096,-13.544c-23.349,9.671 -38.574,32.455 -38.574,57.729l0,375.026c0,34.51 27.977,62.486 62.487,62.486c34.51,-0 62.486,-27.976 62.486,-62.486l0,-224.173l80.843,80.844c24.404,24.402 63.965,24.402 88.369,-0l80.843,-80.844l0,224.173c0,34.51 27.976,62.486 62.486,62.486c34.51,-0 62.486,-27.976 62.486,-62.486l0,-375.026c0,-25.274 -15.224,-48.058 -38.573,-57.729"
|
|
37
|
-
style={pathStyle}
|
|
38
|
-
/>
|
|
39
|
-
</g>
|
|
40
|
-
</svg>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import {PlugIcon} from '@sanity/icons'
|
|
2
|
-
import {Button, Card, Flex, Grid, Heading, Inline, Text} from '@sanity/ui'
|
|
3
|
-
import {useCallback} from 'react'
|
|
4
|
-
|
|
5
|
-
import {useAccessControl} from '../hooks/useAccessControl'
|
|
6
|
-
import type {SetDialogState} from '../hooks/useDialogState'
|
|
7
|
-
import {type PluginConfig} from '../util/types'
|
|
8
|
-
import MuxLogo from './MuxLogo'
|
|
9
|
-
|
|
10
|
-
interface OnboardProps {
|
|
11
|
-
setDialogState: SetDialogState
|
|
12
|
-
config: PluginConfig
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default function Onboard(props: OnboardProps) {
|
|
16
|
-
const {setDialogState} = props
|
|
17
|
-
const handleOpen = useCallback(() => setDialogState('secrets'), [setDialogState])
|
|
18
|
-
const {hasConfigAccess} = useAccessControl(props.config)
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<>
|
|
22
|
-
<div style={{padding: 2}}>
|
|
23
|
-
<Card
|
|
24
|
-
display="flex"
|
|
25
|
-
sizing="border"
|
|
26
|
-
style={{
|
|
27
|
-
aspectRatio: '16/9',
|
|
28
|
-
width: '100%',
|
|
29
|
-
boxShadow: 'var(--card-bg-color) 0 0 0 2px',
|
|
30
|
-
}}
|
|
31
|
-
paddingX={[2, 3, 4, 4]}
|
|
32
|
-
radius={1}
|
|
33
|
-
tone="transparent"
|
|
34
|
-
>
|
|
35
|
-
<Flex justify="flex-start" align="center">
|
|
36
|
-
<Grid columns={1} gap={[2, 3, 4, 4]}>
|
|
37
|
-
<Inline paddingY={1}>
|
|
38
|
-
<div style={{height: '32px'}}>
|
|
39
|
-
<MuxLogo />
|
|
40
|
-
</div>
|
|
41
|
-
</Inline>
|
|
42
|
-
<Inline paddingY={1}>
|
|
43
|
-
<Heading size={[0, 1, 2, 2]}>
|
|
44
|
-
Upload and preview videos directly from your studio.
|
|
45
|
-
</Heading>
|
|
46
|
-
</Inline>
|
|
47
|
-
<Inline paddingY={1}>
|
|
48
|
-
{hasConfigAccess ? (
|
|
49
|
-
<Button mode="ghost" icon={PlugIcon} text="Configure API" onClick={handleOpen} />
|
|
50
|
-
) : (
|
|
51
|
-
<Card padding={[3, 3, 3]} radius={2} shadow={1} tone="critical">
|
|
52
|
-
<Text>
|
|
53
|
-
You do not have access to configure the Mux API. Please contact your
|
|
54
|
-
administrator.
|
|
55
|
-
</Text>
|
|
56
|
-
</Card>
|
|
57
|
-
)}
|
|
58
|
-
</Inline>
|
|
59
|
-
</Grid>
|
|
60
|
-
</Flex>
|
|
61
|
-
</Card>
|
|
62
|
-
</div>
|
|
63
|
-
</>
|
|
64
|
-
)
|
|
65
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import {ChevronLeftIcon, ChevronRightIcon} from '@sanity/icons'
|
|
2
|
-
import {Button, Label} from '@sanity/ui'
|
|
3
|
-
import {type Dispatch, type SetStateAction, useEffect} from 'react'
|
|
4
|
-
|
|
5
|
-
const PageSelector = (props: {
|
|
6
|
-
page: number
|
|
7
|
-
setPage: Dispatch<SetStateAction<number>>
|
|
8
|
-
total: number
|
|
9
|
-
}) => {
|
|
10
|
-
const page = props.page
|
|
11
|
-
const setPage = props.setPage
|
|
12
|
-
|
|
13
|
-
useEffect(() => {
|
|
14
|
-
// Constraint in bounds.
|
|
15
|
-
const clamped = Math.min(props.total - 1, Math.max(0, page))
|
|
16
|
-
if (page !== clamped) {
|
|
17
|
-
setPage(clamped)
|
|
18
|
-
}
|
|
19
|
-
}, [page, props.total, setPage])
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<>
|
|
23
|
-
<Button
|
|
24
|
-
icon={ChevronLeftIcon}
|
|
25
|
-
mode="bleed"
|
|
26
|
-
padding={3}
|
|
27
|
-
style={{cursor: 'pointer'}}
|
|
28
|
-
disabled={page <= 0}
|
|
29
|
-
onClick={() => {
|
|
30
|
-
setPage((p) => {
|
|
31
|
-
return Math.min(props.total - 1, Math.max(0, p - 1))
|
|
32
|
-
})
|
|
33
|
-
}}
|
|
34
|
-
/>
|
|
35
|
-
<Label muted>
|
|
36
|
-
Page {page + 1}/{props.total}
|
|
37
|
-
</Label>
|
|
38
|
-
<Button
|
|
39
|
-
icon={ChevronRightIcon}
|
|
40
|
-
mode="bleed"
|
|
41
|
-
padding={3}
|
|
42
|
-
style={{cursor: 'pointer'}}
|
|
43
|
-
disabled={page >= props.total - 1}
|
|
44
|
-
onClick={() => {
|
|
45
|
-
setPage((p) => {
|
|
46
|
-
return Math.min(props.total - 1, Math.max(0, p + 1))
|
|
47
|
-
})
|
|
48
|
-
}}
|
|
49
|
-
/>
|
|
50
|
-
</>
|
|
51
|
-
)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export default PageSelector
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import {Card, Text} from '@sanity/ui'
|
|
2
|
-
import React, {useEffect, useMemo, useRef} from 'react'
|
|
3
|
-
|
|
4
|
-
import {useCancelUpload} from '../hooks/useCancelUpload'
|
|
5
|
-
import type {MuxInputProps, PluginConfig, VideoAssetDocument} from '../util/types'
|
|
6
|
-
import {TopControls} from './Player.styled'
|
|
7
|
-
import {UploadProgress} from './UploadProgress'
|
|
8
|
-
import VideoPlayer from './VideoPlayer'
|
|
9
|
-
|
|
10
|
-
interface Props extends Pick<MuxInputProps, 'onChange' | 'readOnly'> {
|
|
11
|
-
buttons?: React.ReactNode
|
|
12
|
-
asset: VideoAssetDocument
|
|
13
|
-
config?: PluginConfig
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const Player = ({asset, buttons, readOnly, onChange, config}: Props) => {
|
|
17
|
-
const isLoading = useMemo<boolean | string>(() => {
|
|
18
|
-
if (asset?.status === 'preparing') {
|
|
19
|
-
return 'Preparing the video'
|
|
20
|
-
}
|
|
21
|
-
if (asset?.status === 'waiting_for_upload') {
|
|
22
|
-
return 'Waiting for upload to start'
|
|
23
|
-
}
|
|
24
|
-
if (asset?.status === 'waiting') {
|
|
25
|
-
return 'Processing upload'
|
|
26
|
-
}
|
|
27
|
-
if (asset?.status === 'ready') {
|
|
28
|
-
return false
|
|
29
|
-
}
|
|
30
|
-
if (typeof asset?.status === 'undefined') {
|
|
31
|
-
return false
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return true
|
|
35
|
-
}, [asset])
|
|
36
|
-
// oxlint-disable-next-line react/react-compiler
|
|
37
|
-
const isPreparingStaticRenditions = useMemo<boolean>(() => {
|
|
38
|
-
// Legacy: If static_renditions has a status field, it was created with mp4_support (deprecated)
|
|
39
|
-
// We don't process this old format, just return false
|
|
40
|
-
// Note: 'disabled' status is valid in the new format when no renditions were requested
|
|
41
|
-
if (
|
|
42
|
-
asset?.data?.static_renditions?.status &&
|
|
43
|
-
asset?.data?.static_renditions?.status !== 'disabled'
|
|
44
|
-
) {
|
|
45
|
-
return false
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Check if any file in static_renditions is still preparing
|
|
49
|
-
const files = asset?.data?.static_renditions?.files
|
|
50
|
-
if (!files || files.length === 0) {
|
|
51
|
-
return false
|
|
52
|
-
}
|
|
53
|
-
return files.some((file) => file.status === 'preparing')
|
|
54
|
-
}, [asset?.data?.static_renditions?.status, asset?.data?.static_renditions?.files])
|
|
55
|
-
const playRef = useRef<HTMLDivElement>(null)
|
|
56
|
-
const muteRef = useRef<HTMLDivElement>(null)
|
|
57
|
-
const handleCancelUpload = useCancelUpload(asset, onChange)
|
|
58
|
-
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
const style = document.createElement('style')
|
|
61
|
-
style.innerHTML = 'button svg { vertical-align: middle; }'
|
|
62
|
-
|
|
63
|
-
if (playRef.current?.shadowRoot) {
|
|
64
|
-
playRef.current.shadowRoot.appendChild(style)
|
|
65
|
-
}
|
|
66
|
-
if (muteRef?.current?.shadowRoot) {
|
|
67
|
-
muteRef.current.shadowRoot.appendChild(style.cloneNode(true))
|
|
68
|
-
}
|
|
69
|
-
}, [])
|
|
70
|
-
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
if (asset?.status === 'errored') {
|
|
73
|
-
handleCancelUpload()
|
|
74
|
-
// @TODO use better error handling
|
|
75
|
-
throw new Error(asset.data?.errors?.messages?.join(' '))
|
|
76
|
-
}
|
|
77
|
-
}, [asset.data?.errors?.messages, asset?.status, handleCancelUpload])
|
|
78
|
-
|
|
79
|
-
if (!asset || !asset.status) {
|
|
80
|
-
return null
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (isLoading) {
|
|
84
|
-
return (
|
|
85
|
-
<UploadProgress
|
|
86
|
-
progress={100}
|
|
87
|
-
filename={asset?.filename}
|
|
88
|
-
text={(isLoading !== true && isLoading) || 'Waiting for Mux to complete the upload'}
|
|
89
|
-
onCancel={readOnly ? undefined : () => handleCancelUpload()}
|
|
90
|
-
/>
|
|
91
|
-
)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return (
|
|
95
|
-
<VideoPlayer asset={asset} hlsConfig={config?.hlsConfig}>
|
|
96
|
-
{buttons && <TopControls slot="top-chrome">{buttons}</TopControls>}
|
|
97
|
-
{isPreparingStaticRenditions && (
|
|
98
|
-
<Card
|
|
99
|
-
padding={2}
|
|
100
|
-
radius={1}
|
|
101
|
-
style={{
|
|
102
|
-
background: 'var(--card-fg-color)',
|
|
103
|
-
position: 'absolute',
|
|
104
|
-
top: '0.5em',
|
|
105
|
-
left: '0.5em',
|
|
106
|
-
}}
|
|
107
|
-
>
|
|
108
|
-
<Text size={1} style={{color: 'var(--card-bg-color)'}}>
|
|
109
|
-
MUX is preparing static renditions, please stand by
|
|
110
|
-
</Text>
|
|
111
|
-
</Card>
|
|
112
|
-
)}
|
|
113
|
-
</VideoPlayer>
|
|
114
|
-
)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export default Player
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
EllipsisHorizontalIcon,
|
|
3
|
-
ImageIcon,
|
|
4
|
-
LockIcon,
|
|
5
|
-
PlugIcon,
|
|
6
|
-
ResetIcon,
|
|
7
|
-
SearchIcon,
|
|
8
|
-
SyncIcon,
|
|
9
|
-
TranslateIcon,
|
|
10
|
-
UploadIcon,
|
|
11
|
-
} from '@sanity/icons'
|
|
12
|
-
import {
|
|
13
|
-
Box,
|
|
14
|
-
Button,
|
|
15
|
-
Card,
|
|
16
|
-
Inline,
|
|
17
|
-
Label,
|
|
18
|
-
Menu,
|
|
19
|
-
MenuDivider,
|
|
20
|
-
MenuItem,
|
|
21
|
-
Popover,
|
|
22
|
-
Text,
|
|
23
|
-
Tooltip,
|
|
24
|
-
useClickOutsideEvent,
|
|
25
|
-
} from '@sanity/ui'
|
|
26
|
-
import {memo, useCallback, useEffect, useMemo, useState} from 'react'
|
|
27
|
-
import {PatchEvent, unset} from 'sanity'
|
|
28
|
-
import {styled} from 'styled-components'
|
|
29
|
-
|
|
30
|
-
import {useAccessControl} from '../hooks/useAccessControl'
|
|
31
|
-
import {type DialogState, type SetDialogState} from '../hooks/useDialogState'
|
|
32
|
-
import {useResyncAsset} from '../hooks/useResyncAsset'
|
|
33
|
-
import {getPlaybackPolicy} from '../util/getPlaybackPolicy'
|
|
34
|
-
import type {MuxInputProps, PluginConfig, VideoAssetDocument} from '../util/types'
|
|
35
|
-
import {FileInputMenuItem} from './FileInputMenuItem'
|
|
36
|
-
|
|
37
|
-
const LockCard = styled(Card)`
|
|
38
|
-
position: absolute;
|
|
39
|
-
top: 0;
|
|
40
|
-
left: 0;
|
|
41
|
-
opacity: 0.6;
|
|
42
|
-
mix-blend-mode: screen;
|
|
43
|
-
background: transparent;
|
|
44
|
-
`
|
|
45
|
-
|
|
46
|
-
const LockButton = styled(Button)`
|
|
47
|
-
background: transparent;
|
|
48
|
-
color: white;
|
|
49
|
-
`
|
|
50
|
-
|
|
51
|
-
// @TODO: add support for audio type (asset._type) when uploading an audio file so we can hide the thumbnail option.
|
|
52
|
-
const isVideoAsset = (asset: VideoAssetDocument) => {
|
|
53
|
-
return asset._type === 'mux.videoAsset'
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function PlayerActionsMenu(
|
|
57
|
-
props: Pick<MuxInputProps, 'onChange' | 'readOnly'> & {
|
|
58
|
-
asset: VideoAssetDocument
|
|
59
|
-
onSelect: (files: File[]) => void
|
|
60
|
-
dialogState: DialogState
|
|
61
|
-
setDialogState: SetDialogState
|
|
62
|
-
config: PluginConfig
|
|
63
|
-
accept: string
|
|
64
|
-
},
|
|
65
|
-
) {
|
|
66
|
-
const {asset, readOnly, dialogState, setDialogState, onChange, onSelect, accept} = props
|
|
67
|
-
const [open, setOpen] = useState(false)
|
|
68
|
-
const [menuElement, setMenuRef] = useState<HTMLDivElement | null>(null)
|
|
69
|
-
const isSigned = useMemo(() => getPlaybackPolicy(asset)?.policy === 'signed', [asset])
|
|
70
|
-
const {hasConfigAccess} = useAccessControl(props.config)
|
|
71
|
-
const {resyncAsset, isResyncing} = useResyncAsset({showToast: true})
|
|
72
|
-
|
|
73
|
-
const onReset = useCallback(() => onChange(PatchEvent.from(unset([]))), [onChange])
|
|
74
|
-
|
|
75
|
-
const handleResync = useCallback(async () => {
|
|
76
|
-
setOpen(false)
|
|
77
|
-
await resyncAsset(asset)
|
|
78
|
-
}, [resyncAsset, asset])
|
|
79
|
-
|
|
80
|
-
useEffect(() => {
|
|
81
|
-
if (open && dialogState) {
|
|
82
|
-
// oxlint-disable-next-line react/react-compiler
|
|
83
|
-
setOpen(false)
|
|
84
|
-
}
|
|
85
|
-
}, [dialogState, open])
|
|
86
|
-
|
|
87
|
-
useClickOutsideEvent(
|
|
88
|
-
() => setOpen(false),
|
|
89
|
-
() => [menuElement],
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<Inline space={1} padding={2}>
|
|
94
|
-
{isSigned && (
|
|
95
|
-
<Tooltip
|
|
96
|
-
animate
|
|
97
|
-
content={
|
|
98
|
-
<Box padding={2}>
|
|
99
|
-
<Text muted size={1}>
|
|
100
|
-
Signed playback policy
|
|
101
|
-
</Text>
|
|
102
|
-
</Box>
|
|
103
|
-
}
|
|
104
|
-
placement="right"
|
|
105
|
-
portal
|
|
106
|
-
>
|
|
107
|
-
<LockCard radius={2} margin={2} scheme="dark" tone="positive">
|
|
108
|
-
<LockButton icon={LockIcon} mode="bleed" tone="positive" />
|
|
109
|
-
</LockCard>
|
|
110
|
-
</Tooltip>
|
|
111
|
-
)}
|
|
112
|
-
<Popover
|
|
113
|
-
animate
|
|
114
|
-
content={
|
|
115
|
-
<Menu ref={setMenuRef}>
|
|
116
|
-
<Box padding={2}>
|
|
117
|
-
<Label muted size={1}>
|
|
118
|
-
Replace
|
|
119
|
-
</Label>
|
|
120
|
-
</Box>
|
|
121
|
-
<FileInputMenuItem
|
|
122
|
-
accept={accept}
|
|
123
|
-
icon={UploadIcon}
|
|
124
|
-
onSelect={onSelect}
|
|
125
|
-
text="Upload"
|
|
126
|
-
disabled={readOnly}
|
|
127
|
-
fontSize={1}
|
|
128
|
-
/>
|
|
129
|
-
<MenuItem
|
|
130
|
-
icon={SearchIcon}
|
|
131
|
-
text="Browse"
|
|
132
|
-
onClick={() => setDialogState('select-video')}
|
|
133
|
-
/>
|
|
134
|
-
{isVideoAsset(asset) && (
|
|
135
|
-
<>
|
|
136
|
-
<MenuItem
|
|
137
|
-
icon={ImageIcon}
|
|
138
|
-
text="Thumbnail"
|
|
139
|
-
onClick={() => setDialogState('edit-thumbnail')}
|
|
140
|
-
/>
|
|
141
|
-
<MenuItem
|
|
142
|
-
icon={TranslateIcon}
|
|
143
|
-
text="Captions"
|
|
144
|
-
onClick={() => setDialogState('edit-captions')}
|
|
145
|
-
/>
|
|
146
|
-
<MenuItem
|
|
147
|
-
icon={SyncIcon}
|
|
148
|
-
text="Resync from Mux"
|
|
149
|
-
onClick={handleResync}
|
|
150
|
-
disabled={readOnly || isResyncing}
|
|
151
|
-
/>
|
|
152
|
-
</>
|
|
153
|
-
)}
|
|
154
|
-
<MenuDivider />
|
|
155
|
-
{hasConfigAccess && (
|
|
156
|
-
<>
|
|
157
|
-
<MenuItem
|
|
158
|
-
icon={PlugIcon}
|
|
159
|
-
text="Configure API"
|
|
160
|
-
onClick={() => setDialogState('secrets')}
|
|
161
|
-
/>
|
|
162
|
-
<MenuDivider />
|
|
163
|
-
</>
|
|
164
|
-
)}
|
|
165
|
-
<MenuItem
|
|
166
|
-
tone="critical"
|
|
167
|
-
icon={ResetIcon}
|
|
168
|
-
text="Clear field"
|
|
169
|
-
onClick={onReset}
|
|
170
|
-
disabled={readOnly}
|
|
171
|
-
/>
|
|
172
|
-
</Menu>
|
|
173
|
-
}
|
|
174
|
-
portal
|
|
175
|
-
open={open}
|
|
176
|
-
>
|
|
177
|
-
<Button
|
|
178
|
-
icon={EllipsisHorizontalIcon}
|
|
179
|
-
mode="ghost"
|
|
180
|
-
fontSize={1}
|
|
181
|
-
onClick={() => {
|
|
182
|
-
setDialogState(false)
|
|
183
|
-
setOpen(true)
|
|
184
|
-
}}
|
|
185
|
-
/>
|
|
186
|
-
</Popover>
|
|
187
|
-
</Inline>
|
|
188
|
-
)
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export default memo(PlayerActionsMenu)
|