From 1005f3dd4d41ea078b6b93769228dc615ab4393b Mon Sep 17 00:00:00 2001 From: Jonny Burger Date: Sun, 10 Nov 2024 11:35:39 +0100 Subject: [PATCH] resolve options --- .../app/components/AudioCodecSelection.tsx | 39 +++++++ .../app/components/AudioOperationOption.tsx | 20 ++++ .../convert/app/components/ConvertForm.tsx | 105 ++++++++++-------- packages/convert/app/components/ConvertUi.tsx | 44 +++++--- .../app/components/SelectionSkeleton.tsx | 11 ++ .../app/components/TrackSelectionLabels.tsx | 24 ++++ .../app/components/VideoCodecSelection.tsx | 39 +++++++ .../app/components/VideoOperationOption.tsx | 20 ++++ .../convert/app/components/ui/skeleton.tsx | 2 +- packages/webcodecs/src/codec-id.ts | 20 ++++ 10 files changed, 259 insertions(+), 65 deletions(-) create mode 100644 packages/convert/app/components/AudioCodecSelection.tsx create mode 100644 packages/convert/app/components/AudioOperationOption.tsx create mode 100644 packages/convert/app/components/SelectionSkeleton.tsx create mode 100644 packages/convert/app/components/TrackSelectionLabels.tsx create mode 100644 packages/convert/app/components/VideoCodecSelection.tsx create mode 100644 packages/convert/app/components/VideoOperationOption.tsx diff --git a/packages/convert/app/components/AudioCodecSelection.tsx b/packages/convert/app/components/AudioCodecSelection.tsx new file mode 100644 index 0000000000..2cb5f43037 --- /dev/null +++ b/packages/convert/app/components/AudioCodecSelection.tsx @@ -0,0 +1,39 @@ +import {AudioOperation} from '@remotion/webcodecs'; +import React from 'react'; +import {AudioOperationOption} from './AudioOperationOption'; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from './ui/select'; + +export const AudioCodecSelection: React.FC<{ + readonly audioTrackOptions: AudioOperation[]; + readonly index: number; + readonly setIndex: (v: number) => void; +}> = ({audioTrackOptions, index, setIndex}) => { + return ( + + ); +}; diff --git a/packages/convert/app/components/AudioOperationOption.tsx b/packages/convert/app/components/AudioOperationOption.tsx new file mode 100644 index 0000000000..6ff8ee3a79 --- /dev/null +++ b/packages/convert/app/components/AudioOperationOption.tsx @@ -0,0 +1,20 @@ +import {AudioOperation} from '@remotion/webcodecs'; +import {renderAudioCodecLabel} from '@remotion/webcodecs/src/codec-id'; + +export const AudioOperationOption: React.FC<{ + readonly operation: AudioOperation; +}> = ({operation}) => { + if (operation.type === 'reencode') { + return renderAudioCodecLabel(operation.audioCodec); + } + + if (operation.type === 'copy') { + return 'Copy'; + } + + if (operation.type === 'drop') { + return 'Drop audio track'; + } + + throw new Error('Unknown operation type'); +}; diff --git a/packages/convert/app/components/ConvertForm.tsx b/packages/convert/app/components/ConvertForm.tsx index 511d98fc52..022e51eeb3 100644 --- a/packages/convert/app/components/ConvertForm.tsx +++ b/packages/convert/app/components/ConvertForm.tsx @@ -1,11 +1,10 @@ -import { - ConvertMediaAudioCodec, - ConvertMediaContainer, - ConvertMediaVideoCodec, -} from '@remotion/webcodecs'; +import {ConvertMediaContainer} from '@remotion/webcodecs'; import React from 'react'; import {Container} from '~/lib/generate-new-name'; +import {AudioCodecSelection} from './AudioCodecSelection'; import {SupportedConfigs} from './get-supported-configs'; +import {SelectionSkeleton} from './SelectionSkeleton'; +import {VideoTrackLabel} from './TrackSelectionLabels'; import {Checkbox} from './ui/checkbox'; import {Label} from './ui/label'; import { @@ -16,39 +15,37 @@ import { SelectTrigger, SelectValue, } from './ui/select'; +import {VideoCodecSelection} from './VideoCodecSelection'; export const ConvertForm: React.FC<{ readonly container: ConvertMediaContainer; readonly setContainer: React.Dispatch< React.SetStateAction >; - readonly videoCodec: ConvertMediaVideoCodec; - readonly setVideoCodec: React.Dispatch< - React.SetStateAction - >; - readonly audioCodec: ConvertMediaAudioCodec; - readonly setAudioCodec: React.Dispatch< - React.SetStateAction - >; readonly flipHorizontal: boolean; readonly setFlipHorizontal: React.Dispatch>; readonly flipVertical: boolean; readonly setFlipVertical: React.Dispatch>; readonly supportedConfigs: SupportedConfigs | null; + readonly videoConfigIndex: Record; + readonly audioConfigIndex: Record; + readonly setAudioConfigIndex: (trackId: number, i: number) => void; + readonly setVideoConfigIndex: (trackId: number, i: number) => void; }> = ({ container, setContainer, - setVideoCodec, - videoCodec, - audioCodec, - setAudioCodec, flipHorizontal, flipVertical, setFlipHorizontal, setFlipVertical, supportedConfigs, + audioConfigIndex, + setAudioConfigIndex, + videoConfigIndex, + setVideoConfigIndex, }) => { const [showAdvanced, setShowAdvanced] = React.useState(false); + console.log(supportedConfigs); return ( @@ -69,38 +66,48 @@ export const ConvertForm: React.FC<{ -
- - -
-
- - -
+ {supportedConfigs ? ( + supportedConfigs.videoTrackOptions.map((track) => { + return ( +
+ + { + setVideoConfigIndex(track.trackId, i); + }} + videoOperations={track.operations} + /> +
+ ); + }) + ) : ( + + )} + {supportedConfigs ? ( + supportedConfigs.audioTrackOptions.map((track) => { + return ( +
+ + { + setAudioConfigIndex(track.trackId, i); + }} + audioTrackOptions={track.operations} + /> +
+ ); + }) + ) : ( + + )} {showAdvanced ? ( <>
diff --git a/packages/convert/app/components/ConvertUi.tsx b/packages/convert/app/components/ConvertUi.tsx index a69f3c72f3..89f23376f3 100644 --- a/packages/convert/app/components/ConvertUi.tsx +++ b/packages/convert/app/components/ConvertUi.tsx @@ -2,12 +2,7 @@ import {Button} from '@/components/ui/button'; import {CardTitle} from '@/components/ui/card'; import {fetchReader} from '@remotion/media-parser/fetch'; import {webFileReader} from '@remotion/media-parser/web-file'; -import { - convertMedia, - ConvertMediaAudioCodec, - ConvertMediaContainer, - ConvertMediaVideoCodec, -} from '@remotion/webcodecs'; +import {convertMedia, ConvertMediaContainer} from '@remotion/webcodecs'; import {useCallback, useEffect, useRef, useState} from 'react'; import {ConvertState, Source} from '~/lib/convert-state'; import {getNewName} from '~/lib/generate-new-name'; @@ -26,13 +21,31 @@ export default function ConvertUI({ readonly supportedConfigs: SupportedConfigs | null; }) { const [container, setContainer] = useState('webm'); - const [videoCodec, setVideoCodec] = useState('vp8'); - const [audioCodec, setAudioCodec] = useState('opus'); + const [videoConfigIndex, _setVideoConfigIndex] = useState< + Record + >({}); + const [audioConfigIndex, _setAudioConfigIndex] = useState< + Record + >({}); const [state, setState] = useState({type: 'idle'}); const [name, setName] = useState(null); const [flipHorizontal, setFlipHorizontal] = useState(false); const [flipVertical, setFlipVertical] = useState(false); + const setVideoConfigIndex = useCallback((trackId: number, i: number) => { + _setVideoConfigIndex((prev) => ({ + ...prev, + [trackId]: i, + })); + }, []); + + const setAudioConfigIndex = useCallback((trackId: number, i: number) => { + _setAudioConfigIndex((prev) => ({ + ...prev, + [trackId]: i, + })); + }, []); + const abortSignal = useRef(null); const onClick = useCallback(() => { @@ -69,8 +82,9 @@ export default function ConvertUI({ }, }); }, - videoCodec, - audioCodec, + // TODO: This should be optional + videoCodec: 'vp8', + audioCodec: 'opus', container: container as 'webm', signal: abortController.signal, fields: { @@ -118,7 +132,7 @@ export default function ConvertUI({ return () => { abortController.abort(); }; - }, [src, videoCodec, audioCodec, container, flipHorizontal, flipVertical]); + }, [src, container, flipHorizontal, flipVertical]); const cancel = useCallback(() => { if (state.type !== 'in-progress') { @@ -206,15 +220,15 @@ export default function ConvertUI({ {...{ container, setContainer, - setVideoCodec, - videoCodec, - audioCodec, - setAudioCodec, flipHorizontal, flipVertical, setFlipHorizontal, setFlipVertical, supportedConfigs, + audioConfigIndex, + videoConfigIndex, + setAudioConfigIndex, + setVideoConfigIndex, }} />
diff --git a/packages/convert/app/components/SelectionSkeleton.tsx b/packages/convert/app/components/SelectionSkeleton.tsx new file mode 100644 index 0000000000..2ae1ed8cc8 --- /dev/null +++ b/packages/convert/app/components/SelectionSkeleton.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import {Skeleton} from './ui/skeleton'; + +export const SelectionSkeleton: React.FC = () => { + return ( +
+ + +
+ ); +}; diff --git a/packages/convert/app/components/TrackSelectionLabels.tsx b/packages/convert/app/components/TrackSelectionLabels.tsx new file mode 100644 index 0000000000..bd575ec641 --- /dev/null +++ b/packages/convert/app/components/TrackSelectionLabels.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import {Label} from './ui/label'; + +export const VideoTrackLabel: React.FC<{ + readonly trackId: number; + readonly totalVideoTracks: number; +}> = ({trackId, totalVideoTracks}) => { + if (totalVideoTracks === 1) { + return ; + } + + return ; +}; + +export const AudioTrackLabel: React.FC<{ + readonly trackId: number; + readonly totalAudioTracks: number; +}> = ({trackId, totalAudioTracks}) => { + if (totalAudioTracks === 1) { + return ; + } + + return ; +}; diff --git a/packages/convert/app/components/VideoCodecSelection.tsx b/packages/convert/app/components/VideoCodecSelection.tsx new file mode 100644 index 0000000000..77c5911094 --- /dev/null +++ b/packages/convert/app/components/VideoCodecSelection.tsx @@ -0,0 +1,39 @@ +import {VideoOperation} from '@remotion/webcodecs'; +import React from 'react'; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from './ui/select'; +import {VideoOperationOption} from './VideoOperationOption'; + +export const VideoCodecSelection: React.FC<{ + readonly videoOperations: VideoOperation[]; + readonly index: number; + readonly setIndex: (v: number) => void; +}> = ({videoOperations, index, setIndex}) => { + return ( + + ); +}; diff --git a/packages/convert/app/components/VideoOperationOption.tsx b/packages/convert/app/components/VideoOperationOption.tsx new file mode 100644 index 0000000000..cb0eca0292 --- /dev/null +++ b/packages/convert/app/components/VideoOperationOption.tsx @@ -0,0 +1,20 @@ +import {VideoOperation} from '@remotion/webcodecs'; +import {renderVideoCodecLabel} from '@remotion/webcodecs/src/codec-id'; + +export const VideoOperationOption: React.FC<{ + readonly operation: VideoOperation; +}> = ({operation}) => { + if (operation.type === 'reencode') { + return renderVideoCodecLabel(operation.videoCodec); + } + + if (operation.type === 'copy') { + return 'Copy'; + } + + if (operation.type === 'drop') { + return 'Drop video track'; + } + + throw new Error('Unknown operation type'); +}; diff --git a/packages/convert/app/components/ui/skeleton.tsx b/packages/convert/app/components/ui/skeleton.tsx index 0b861676c6..f674e0e26b 100644 --- a/packages/convert/app/components/ui/skeleton.tsx +++ b/packages/convert/app/components/ui/skeleton.tsx @@ -3,7 +3,7 @@ import {cn} from '~/lib/utils'; function Skeleton({className, ...props}: React.HTMLAttributes) { return (
); diff --git a/packages/webcodecs/src/codec-id.ts b/packages/webcodecs/src/codec-id.ts index 7a51861667..c1eab74ff0 100644 --- a/packages/webcodecs/src/codec-id.ts +++ b/packages/webcodecs/src/codec-id.ts @@ -2,6 +2,26 @@ const availableVideoCodecs = ['vp8', 'vp9'] as const; export const getAvailableVideoCodecs = () => availableVideoCodecs; export type ConvertMediaVideoCodec = (typeof availableVideoCodecs)[number]; +export const renderVideoCodecLabel = (codec: ConvertMediaVideoCodec) => { + if (codec === 'vp8') { + return 'VP8'; + } + + if (codec === 'vp9') { + return 'VP9'; + } + + throw new Error(`Unknown video codec ${codec satisfies never}`); +}; + const availableAudioCodecs = ['opus'] as const; export const getAvailableAudioCodecs = () => availableAudioCodecs; export type ConvertMediaAudioCodec = (typeof availableAudioCodecs)[number]; + +export const renderAudioCodecLabel = (codec: ConvertMediaAudioCodec) => { + if (codec === 'opus') { + return 'Opus'; + } + + throw new Error(`Unknown audio codec ${codec satisfies never}`); +};