@kolbo-cli/kolbo-linux-x64 0.0.0-dev-202604101332 → 0.0.0-dev-202604101600

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.
Files changed (47) hide show
  1. package/bin/kolbo +0 -0
  2. package/package.json +1 -1
  3. package/skills/frontend-design/SKILL.md +42 -0
  4. package/skills/kolbo/SKILL.md +61 -0
  5. package/skills/remotion-best-practices/SKILL.md +61 -0
  6. package/skills/remotion-best-practices/rules/3d.md +86 -0
  7. package/skills/remotion-best-practices/rules/animations.md +27 -0
  8. package/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
  9. package/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
  10. package/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +103 -0
  11. package/skills/remotion-best-practices/rules/assets.md +78 -0
  12. package/skills/remotion-best-practices/rules/audio-visualization.md +198 -0
  13. package/skills/remotion-best-practices/rules/audio.md +169 -0
  14. package/skills/remotion-best-practices/rules/calculate-metadata.md +134 -0
  15. package/skills/remotion-best-practices/rules/can-decode.md +81 -0
  16. package/skills/remotion-best-practices/rules/charts.md +120 -0
  17. package/skills/remotion-best-practices/rules/compositions.md +154 -0
  18. package/skills/remotion-best-practices/rules/display-captions.md +184 -0
  19. package/skills/remotion-best-practices/rules/extract-frames.md +229 -0
  20. package/skills/remotion-best-practices/rules/ffmpeg.md +38 -0
  21. package/skills/remotion-best-practices/rules/fonts.md +152 -0
  22. package/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
  23. package/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
  24. package/skills/remotion-best-practices/rules/get-video-duration.md +60 -0
  25. package/skills/remotion-best-practices/rules/gifs.md +141 -0
  26. package/skills/remotion-best-practices/rules/images.md +134 -0
  27. package/skills/remotion-best-practices/rules/import-srt-captions.md +69 -0
  28. package/skills/remotion-best-practices/rules/light-leaks.md +73 -0
  29. package/skills/remotion-best-practices/rules/lottie.md +70 -0
  30. package/skills/remotion-best-practices/rules/maps.md +412 -0
  31. package/skills/remotion-best-practices/rules/measuring-dom-nodes.md +34 -0
  32. package/skills/remotion-best-practices/rules/measuring-text.md +140 -0
  33. package/skills/remotion-best-practices/rules/parameters.md +109 -0
  34. package/skills/remotion-best-practices/rules/sequencing.md +118 -0
  35. package/skills/remotion-best-practices/rules/sfx.md +30 -0
  36. package/skills/remotion-best-practices/rules/subtitles.md +36 -0
  37. package/skills/remotion-best-practices/rules/tailwind.md +11 -0
  38. package/skills/remotion-best-practices/rules/text-animations.md +20 -0
  39. package/skills/remotion-best-practices/rules/timing.md +179 -0
  40. package/skills/remotion-best-practices/rules/transcribe-captions.md +70 -0
  41. package/skills/remotion-best-practices/rules/transitions.md +197 -0
  42. package/skills/remotion-best-practices/rules/transparent-videos.md +106 -0
  43. package/skills/remotion-best-practices/rules/trimming.md +51 -0
  44. package/skills/remotion-best-practices/rules/videos.md +171 -0
  45. package/skills/remotion-best-practices/rules/voiceover.md +99 -0
  46. package/skills/video-production/SKILL.md +152 -0
  47. package/skills/youtube-clipper/SKILL.md +187 -0
@@ -0,0 +1,78 @@
1
+ ---
2
+ name: assets
3
+ description: Importing images, videos, audio, and fonts into Remotion
4
+ metadata:
5
+ tags: assets, staticFile, images, fonts, public
6
+ ---
7
+
8
+ # Importing assets in Remotion
9
+
10
+ ## The public folder
11
+
12
+ Place assets in the `public/` folder at your project root.
13
+
14
+ ## Using staticFile()
15
+
16
+ You MUST use `staticFile()` to reference files from the `public/` folder:
17
+
18
+ ```tsx
19
+ import { Img, staticFile } from "remotion";
20
+
21
+ export const MyComposition = () => {
22
+ return <Img src={staticFile("logo.png")} />;
23
+ };
24
+ ```
25
+
26
+ The function returns an encoded URL that works correctly when deploying to subdirectories.
27
+
28
+ ## Using with components
29
+
30
+ **Images:**
31
+
32
+ ```tsx
33
+ import { Img, staticFile } from "remotion";
34
+
35
+ <Img src={staticFile("photo.png")} />;
36
+ ```
37
+
38
+ **Videos:**
39
+
40
+ ```tsx
41
+ import { Video } from "@remotion/media";
42
+ import { staticFile } from "remotion";
43
+
44
+ <Video src={staticFile("clip.mp4")} />;
45
+ ```
46
+
47
+ **Audio:**
48
+
49
+ ```tsx
50
+ import { Audio } from "@remotion/media";
51
+ import { staticFile } from "remotion";
52
+
53
+ <Audio src={staticFile("music.mp3")} />;
54
+ ```
55
+
56
+ **Fonts:**
57
+
58
+ ```tsx
59
+ import { staticFile } from "remotion";
60
+
61
+ const fontFamily = new FontFace("MyFont", `url(${staticFile("font.woff2")})`);
62
+ await fontFamily.load();
63
+ document.fonts.add(fontFamily);
64
+ ```
65
+
66
+ ## Remote URLs
67
+
68
+ Remote URLs can be used directly without `staticFile()`:
69
+
70
+ ```tsx
71
+ <Img src="https://example.com/image.png" />
72
+ <Video src="https://remotion.media/video.mp4" />
73
+ ```
74
+
75
+ ## Important notes
76
+
77
+ - Remotion components (`<Img>`, `<Video>`, `<Audio>`) ensure assets are fully loaded before rendering
78
+ - Special characters in filenames (`#`, `?`, `&`) are automatically encoded
@@ -0,0 +1,198 @@
1
+ ---
2
+ name: audio-visualization
3
+ description: Audio visualization patterns - spectrum bars, waveforms, bass-reactive effects
4
+ metadata:
5
+ tags: audio, visualization, spectrum, waveform, bass, music, audiogram, frequency
6
+ ---
7
+
8
+ # Audio Visualization in Remotion
9
+
10
+ ## Prerequisites
11
+
12
+ ```bash
13
+ npx remotion add @remotion/media-utils
14
+ ```
15
+
16
+ ## Loading Audio Data
17
+
18
+ Use `useWindowedAudioData()` (https://www.remotion.dev/docs/use-windowed-audio-data) to load audio data:
19
+
20
+ ```tsx
21
+ import { useWindowedAudioData } from "@remotion/media-utils";
22
+ import { staticFile, useCurrentFrame, useVideoConfig } from "remotion";
23
+
24
+ const frame = useCurrentFrame();
25
+ const { fps } = useVideoConfig();
26
+
27
+ const { audioData, dataOffsetInSeconds } = useWindowedAudioData({
28
+ src: staticFile("podcast.wav"),
29
+ frame,
30
+ fps,
31
+ windowInSeconds: 30,
32
+ });
33
+ ```
34
+
35
+ ## Spectrum Bar Visualization
36
+
37
+ Use `visualizeAudio()` (https://www.remotion.dev/docs/visualize-audio) to get frequency data for bar charts:
38
+
39
+ ```tsx
40
+ import { useWindowedAudioData, visualizeAudio } from "@remotion/media-utils";
41
+ import { staticFile, useCurrentFrame, useVideoConfig } from "remotion";
42
+
43
+ const frame = useCurrentFrame();
44
+ const { fps } = useVideoConfig();
45
+
46
+ const { audioData, dataOffsetInSeconds } = useWindowedAudioData({
47
+ src: staticFile("music.mp3"),
48
+ frame,
49
+ fps,
50
+ windowInSeconds: 30,
51
+ });
52
+
53
+ if (!audioData) {
54
+ return null;
55
+ }
56
+
57
+ const frequencies = visualizeAudio({
58
+ fps,
59
+ frame,
60
+ audioData,
61
+ numberOfSamples: 256,
62
+ optimizeFor: "speed",
63
+ dataOffsetInSeconds,
64
+ });
65
+
66
+ return (
67
+ <div style={{ display: "flex", alignItems: "flex-end", height: 200 }}>
68
+ {frequencies.map((v, i) => (
69
+ <div
70
+ key={i}
71
+ style={{
72
+ flex: 1,
73
+ height: `${v * 100}%`,
74
+ backgroundColor: "#0b84f3",
75
+ margin: "0 1px",
76
+ }}
77
+ />
78
+ ))}
79
+ </div>
80
+ );
81
+ ```
82
+
83
+ - `numberOfSamples` must be power of 2 (32, 64, 128, 256, 512, 1024)
84
+ - Values range 0-1; left of array = bass, right = highs
85
+ - Use `optimizeFor: "speed"` for Lambda or high sample counts
86
+
87
+ **Important:** When passing `audioData` to child components, also pass the `frame` from the parent. Do not call `useCurrentFrame()` in each child - this causes discontinuous visualization when children are inside `<Sequence>` with offsets.
88
+
89
+ ## Waveform Visualization
90
+
91
+ Use `visualizeAudioWaveform()` (https://www.remotion.dev/docs/media-utils/visualize-audio-waveform) with `createSmoothSvgPath()` (https://www.remotion.dev/docs/media-utils/create-smooth-svg-path) for oscilloscope-style displays:
92
+
93
+ ```tsx
94
+ import {
95
+ createSmoothSvgPath,
96
+ useWindowedAudioData,
97
+ visualizeAudioWaveform,
98
+ } from "@remotion/media-utils";
99
+ import { staticFile, useCurrentFrame, useVideoConfig } from "remotion";
100
+
101
+ const frame = useCurrentFrame();
102
+ const { width, fps } = useVideoConfig();
103
+ const HEIGHT = 200;
104
+
105
+ const { audioData, dataOffsetInSeconds } = useWindowedAudioData({
106
+ src: staticFile("voice.wav"),
107
+ frame,
108
+ fps,
109
+ windowInSeconds: 30,
110
+ });
111
+
112
+ if (!audioData) {
113
+ return null;
114
+ }
115
+
116
+ const waveform = visualizeAudioWaveform({
117
+ fps,
118
+ frame,
119
+ audioData,
120
+ numberOfSamples: 256,
121
+ windowInSeconds: 0.5,
122
+ dataOffsetInSeconds,
123
+ });
124
+
125
+ const path = createSmoothSvgPath({
126
+ points: waveform.map((y, i) => ({
127
+ x: (i / (waveform.length - 1)) * width,
128
+ y: HEIGHT / 2 + (y * HEIGHT) / 2,
129
+ })),
130
+ });
131
+
132
+ return (
133
+ <svg width={width} height={HEIGHT}>
134
+ <path d={path} fill="none" stroke="#0b84f3" strokeWidth={2} />
135
+ </svg>
136
+ );
137
+ ```
138
+
139
+ ## Bass-Reactive Effects
140
+
141
+ Extract low frequencies for beat-reactive animations:
142
+
143
+ ```tsx
144
+ const frequencies = visualizeAudio({
145
+ fps,
146
+ frame,
147
+ audioData,
148
+ numberOfSamples: 128,
149
+ optimizeFor: "speed",
150
+ dataOffsetInSeconds,
151
+ });
152
+
153
+ const lowFrequencies = frequencies.slice(0, 32);
154
+ const bassIntensity =
155
+ lowFrequencies.reduce((sum, v) => sum + v, 0) / lowFrequencies.length;
156
+
157
+ const scale = 1 + bassIntensity * 0.5;
158
+ const opacity = Math.min(0.6, bassIntensity * 0.8);
159
+ ```
160
+
161
+ ## Volume-Based Waveform
162
+
163
+ Use `getWaveformPortion()` (https://www.remotion.dev/docs/get-waveform-portion) when you need simplified volume data instead of frequency spectrum:
164
+
165
+ ```tsx
166
+ import { getWaveformPortion } from "@remotion/media-utils";
167
+ import { useCurrentFrame, useVideoConfig } from "remotion";
168
+
169
+ const frame = useCurrentFrame();
170
+ const { fps } = useVideoConfig();
171
+ const currentTimeInSeconds = frame / fps;
172
+
173
+ const waveform = getWaveformPortion({
174
+ audioData,
175
+ startTimeInSeconds: currentTimeInSeconds,
176
+ durationInSeconds: 5,
177
+ numberOfSamples: 50,
178
+ });
179
+
180
+ // Returns array of { index, amplitude } objects (amplitude: 0-1)
181
+ waveform.map((bar) => (
182
+ <div key={bar.index} style={{ height: bar.amplitude * 100 }} />
183
+ ));
184
+ ```
185
+
186
+ ## Postprocessing
187
+
188
+ Low frequencies naturally dominate. Apply logarithmic scaling for visual balance:
189
+
190
+ ```tsx
191
+ const minDb = -100;
192
+ const maxDb = -30;
193
+
194
+ const scaled = frequencies.map((value) => {
195
+ const db = 20 * Math.log10(value);
196
+ return (db - minDb) / (maxDb - minDb);
197
+ });
198
+ ```
@@ -0,0 +1,169 @@
1
+ ---
2
+ name: audio
3
+ description: Using audio and sound in Remotion - importing, trimming, volume, speed, pitch
4
+ metadata:
5
+ tags: audio, media, trim, volume, speed, loop, pitch, mute, sound, sfx
6
+ ---
7
+
8
+ # Using audio in Remotion
9
+
10
+ ## Prerequisites
11
+
12
+ First, the @remotion/media package needs to be installed.
13
+ If it is not installed, use the following command:
14
+
15
+ ```bash
16
+ npx remotion add @remotion/media
17
+ ```
18
+
19
+ ## Importing Audio
20
+
21
+ Use `<Audio>` from `@remotion/media` to add audio to your composition.
22
+
23
+ ```tsx
24
+ import { Audio } from "@remotion/media";
25
+ import { staticFile } from "remotion";
26
+
27
+ export const MyComposition = () => {
28
+ return <Audio src={staticFile("audio.mp3")} />;
29
+ };
30
+ ```
31
+
32
+ Remote URLs are also supported:
33
+
34
+ ```tsx
35
+ <Audio src="https://remotion.media/audio.mp3" />
36
+ ```
37
+
38
+ By default, audio plays from the start, at full volume and full length.
39
+ Multiple audio tracks can be layered by adding multiple `<Audio>` components.
40
+
41
+ ## Trimming
42
+
43
+ Use `trimBefore` and `trimAfter` to remove portions of the audio. Values are in frames.
44
+
45
+ ```tsx
46
+ const { fps } = useVideoConfig();
47
+
48
+ return (
49
+ <Audio
50
+ src={staticFile("audio.mp3")}
51
+ trimBefore={2 * fps} // Skip the first 2 seconds
52
+ trimAfter={10 * fps} // End at the 10 second mark
53
+ />
54
+ );
55
+ ```
56
+
57
+ The audio still starts playing at the beginning of the composition - only the specified portion is played.
58
+
59
+ ## Delaying
60
+
61
+ Wrap the audio in a `<Sequence>` to delay when it starts:
62
+
63
+ ```tsx
64
+ import { Sequence, staticFile } from "remotion";
65
+ import { Audio } from "@remotion/media";
66
+
67
+ const { fps } = useVideoConfig();
68
+
69
+ return (
70
+ <Sequence from={1 * fps}>
71
+ <Audio src={staticFile("audio.mp3")} />
72
+ </Sequence>
73
+ );
74
+ ```
75
+
76
+ The audio will start playing after 1 second.
77
+
78
+ ## Volume
79
+
80
+ Set a static volume (0 to 1):
81
+
82
+ ```tsx
83
+ <Audio src={staticFile("audio.mp3")} volume={0.5} />
84
+ ```
85
+
86
+ Or use a callback for dynamic volume based on the current frame:
87
+
88
+ ```tsx
89
+ import { interpolate } from "remotion";
90
+
91
+ const { fps } = useVideoConfig();
92
+
93
+ return (
94
+ <Audio
95
+ src={staticFile("audio.mp3")}
96
+ volume={(f) =>
97
+ interpolate(f, [0, 1 * fps], [0, 1], { extrapolateRight: "clamp" })
98
+ }
99
+ />
100
+ );
101
+ ```
102
+
103
+ The value of `f` starts at 0 when the audio begins to play, not the composition frame.
104
+
105
+ ## Muting
106
+
107
+ Use `muted` to silence the audio. It can be set dynamically:
108
+
109
+ ```tsx
110
+ const frame = useCurrentFrame();
111
+ const { fps } = useVideoConfig();
112
+
113
+ return (
114
+ <Audio
115
+ src={staticFile("audio.mp3")}
116
+ muted={frame >= 2 * fps && frame <= 4 * fps} // Mute between 2s and 4s
117
+ />
118
+ );
119
+ ```
120
+
121
+ ## Speed
122
+
123
+ Use `playbackRate` to change the playback speed:
124
+
125
+ ```tsx
126
+ <Audio src={staticFile("audio.mp3")} playbackRate={2} /> {/* 2x speed */}
127
+ <Audio src={staticFile("audio.mp3")} playbackRate={0.5} /> {/* Half speed */}
128
+ ```
129
+
130
+ Reverse playback is not supported.
131
+
132
+ ## Looping
133
+
134
+ Use `loop` to loop the audio indefinitely:
135
+
136
+ ```tsx
137
+ <Audio src={staticFile("audio.mp3")} loop />
138
+ ```
139
+
140
+ Use `loopVolumeCurveBehavior` to control how the frame count behaves when looping:
141
+
142
+ - `"repeat"`: Frame count resets to 0 each loop (default)
143
+ - `"extend"`: Frame count continues incrementing
144
+
145
+ ```tsx
146
+ <Audio
147
+ src={staticFile("audio.mp3")}
148
+ loop
149
+ loopVolumeCurveBehavior="extend"
150
+ volume={(f) => interpolate(f, [0, 300], [1, 0])} // Fade out over multiple loops
151
+ />
152
+ ```
153
+
154
+ ## Pitch
155
+
156
+ Use `toneFrequency` to adjust the pitch without affecting speed. Values range from 0.01 to 2:
157
+
158
+ ```tsx
159
+ <Audio
160
+ src={staticFile("audio.mp3")}
161
+ toneFrequency={1.5} // Higher pitch
162
+ />
163
+ <Audio
164
+ src={staticFile("audio.mp3")}
165
+ toneFrequency={0.8} // Lower pitch
166
+ />
167
+ ```
168
+
169
+ Pitch shifting only works during server-side rendering, not in the Remotion Studio preview or in the `<Player />`.
@@ -0,0 +1,134 @@
1
+ ---
2
+ name: calculate-metadata
3
+ description: Dynamically set composition duration, dimensions, and props
4
+ metadata:
5
+ tags: calculateMetadata, duration, dimensions, props, dynamic
6
+ ---
7
+
8
+ # Using calculateMetadata
9
+
10
+ Use `calculateMetadata` on a `<Composition>` to dynamically set duration, dimensions, and transform props before rendering.
11
+
12
+ ```tsx
13
+ <Composition
14
+ id="MyComp"
15
+ component={MyComponent}
16
+ durationInFrames={300}
17
+ fps={30}
18
+ width={1920}
19
+ height={1080}
20
+ defaultProps={{ videoSrc: "https://remotion.media/video.mp4" }}
21
+ calculateMetadata={calculateMetadata}
22
+ />
23
+ ```
24
+
25
+ ## Setting duration based on a video
26
+
27
+ Use the [`getVideoDuration`](./get-video-duration.md) and [`getVideoDimensions`](./get-video-dimensions.md) skills to get the video duration and dimensions:
28
+
29
+ ```tsx
30
+ import { CalculateMetadataFunction } from "remotion";
31
+ import { getVideoDuration } from "./get-video-duration";
32
+
33
+ const calculateMetadata: CalculateMetadataFunction<Props> = async ({
34
+ props,
35
+ }) => {
36
+ const durationInSeconds = await getVideoDuration(props.videoSrc);
37
+
38
+ return {
39
+ durationInFrames: Math.ceil(durationInSeconds * 30),
40
+ };
41
+ };
42
+ ```
43
+
44
+ ## Matching dimensions of a video
45
+
46
+ Use the [`getVideoDimensions`](./get-video-dimensions.md) skill to get the video dimensions:
47
+
48
+ ```tsx
49
+ import { CalculateMetadataFunction } from "remotion";
50
+ import { getVideoDuration } from "./get-video-duration";
51
+ import { getVideoDimensions } from "./get-video-dimensions";
52
+
53
+ const calculateMetadata: CalculateMetadataFunction<Props> = async ({
54
+ props,
55
+ }) => {
56
+ const dimensions = await getVideoDimensions(props.videoSrc);
57
+
58
+ return {
59
+ width: dimensions.width,
60
+ height: dimensions.height,
61
+ };
62
+ };
63
+ ```
64
+
65
+ ## Setting duration based on multiple videos
66
+
67
+ ```tsx
68
+ const calculateMetadata: CalculateMetadataFunction<Props> = async ({
69
+ props,
70
+ }) => {
71
+ const metadataPromises = props.videos.map((video) =>
72
+ getVideoDuration(video.src),
73
+ );
74
+ const allMetadata = await Promise.all(metadataPromises);
75
+
76
+ const totalDuration = allMetadata.reduce(
77
+ (sum, durationInSeconds) => sum + durationInSeconds,
78
+ 0,
79
+ );
80
+
81
+ return {
82
+ durationInFrames: Math.ceil(totalDuration * 30),
83
+ };
84
+ };
85
+ ```
86
+
87
+ ## Setting a default outName
88
+
89
+ Set the default output filename based on props:
90
+
91
+ ```tsx
92
+ const calculateMetadata: CalculateMetadataFunction<Props> = async ({
93
+ props,
94
+ }) => {
95
+ return {
96
+ defaultOutName: `video-${props.id}.mp4`,
97
+ };
98
+ };
99
+ ```
100
+
101
+ ## Transforming props
102
+
103
+ Fetch data or transform props before rendering:
104
+
105
+ ```tsx
106
+ const calculateMetadata: CalculateMetadataFunction<Props> = async ({
107
+ props,
108
+ abortSignal,
109
+ }) => {
110
+ const response = await fetch(props.dataUrl, { signal: abortSignal });
111
+ const data = await response.json();
112
+
113
+ return {
114
+ props: {
115
+ ...props,
116
+ fetchedData: data,
117
+ },
118
+ };
119
+ };
120
+ ```
121
+
122
+ The `abortSignal` cancels stale requests when props change in the Studio.
123
+
124
+ ## Return value
125
+
126
+ All fields are optional. Returned values override the `<Composition>` props:
127
+
128
+ - `durationInFrames`: Number of frames
129
+ - `width`: Composition width in pixels
130
+ - `height`: Composition height in pixels
131
+ - `fps`: Frames per second
132
+ - `props`: Transformed props passed to the component
133
+ - `defaultOutName`: Default output filename
134
+ - `defaultCodec`: Default codec for rendering
@@ -0,0 +1,81 @@
1
+ ---
2
+ name: can-decode
3
+ description: Check if a video can be decoded by the browser using Mediabunny
4
+ metadata:
5
+ tags: decode, validation, video, audio, compatibility, browser
6
+ ---
7
+
8
+ # Checking if a video can be decoded
9
+
10
+ Use Mediabunny to check if a video can be decoded by the browser before attempting to play it.
11
+
12
+ First, install the right version of Mediabunny:
13
+
14
+ ```bash
15
+ npx remotion add mediabunny
16
+ ```
17
+
18
+ ## The `canDecode()` function
19
+
20
+ This function can be copy-pasted into any project.
21
+
22
+ ```tsx
23
+ import { Input, ALL_FORMATS, UrlSource } from "mediabunny";
24
+
25
+ export const canDecode = async (src: string) => {
26
+ const input = new Input({
27
+ formats: ALL_FORMATS,
28
+ source: new UrlSource(src, {
29
+ getRetryDelay: () => null,
30
+ }),
31
+ });
32
+
33
+ try {
34
+ await input.getFormat();
35
+ } catch {
36
+ return false;
37
+ }
38
+
39
+ const videoTrack = await input.getPrimaryVideoTrack();
40
+ if (videoTrack && !(await videoTrack.canDecode())) {
41
+ return false;
42
+ }
43
+
44
+ const audioTrack = await input.getPrimaryAudioTrack();
45
+ if (audioTrack && !(await audioTrack.canDecode())) {
46
+ return false;
47
+ }
48
+
49
+ return true;
50
+ };
51
+ ```
52
+
53
+ ## Usage
54
+
55
+ ```tsx
56
+ const src = "https://remotion.media/video.mp4";
57
+ const isDecodable = await canDecode(src);
58
+
59
+ if (isDecodable) {
60
+ console.log("Video can be decoded");
61
+ } else {
62
+ console.log("Video cannot be decoded by this browser");
63
+ }
64
+ ```
65
+
66
+ ## Using with Blob
67
+
68
+ For file uploads or drag-and-drop, use `BlobSource`:
69
+
70
+ ```tsx
71
+ import { Input, ALL_FORMATS, BlobSource } from "mediabunny";
72
+
73
+ export const canDecodeBlob = async (blob: Blob) => {
74
+ const input = new Input({
75
+ formats: ALL_FORMATS,
76
+ source: new BlobSource(blob),
77
+ });
78
+
79
+ // Same validation logic as above
80
+ };
81
+ ```