@kortix/sandbox 0.4.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/config/customize.sh +143 -0
- package/config/kortix-env-setup.sh +25 -0
- package/kortix-master/package.json +22 -0
- package/kortix-master/src/config.ts +22 -0
- package/kortix-master/src/index.ts +44 -0
- package/kortix-master/src/routes/env.ts +65 -0
- package/kortix-master/src/routes/proxy.ts +108 -0
- package/kortix-master/src/routes/update.ts +185 -0
- package/kortix-master/src/services/proxy.ts +43 -0
- package/kortix-master/src/services/secret-store.ts +156 -0
- package/kortix-master/tsconfig.json +14 -0
- package/opencode/agents/kortix-browser.md +142 -0
- package/opencode/agents/kortix-build.md +62 -0
- package/opencode/agents/kortix-explore.md +66 -0
- package/opencode/agents/kortix-image-gen.md +33 -0
- package/opencode/agents/kortix-main.md +450 -0
- package/opencode/agents/kortix-plan.md +100 -0
- package/opencode/agents/kortix-research.md +84 -0
- package/opencode/agents/kortix-sheets.md +61 -0
- package/opencode/agents/kortix-slides.md +64 -0
- package/opencode/agents/kortix-web-dev.md +572 -0
- package/opencode/commands/email.md +36 -0
- package/opencode/commands/init.md +43 -0
- package/opencode/commands/journal.md +44 -0
- package/opencode/commands/memory-init.md +81 -0
- package/opencode/commands/memory-search.md +50 -0
- package/opencode/commands/memory-status.md +56 -0
- package/opencode/commands/research.md +36 -0
- package/opencode/commands/search.md +38 -0
- package/opencode/commands/slides.md +32 -0
- package/opencode/commands/spreadsheet.md +30 -0
- package/opencode/memory.json +37 -0
- package/opencode/ocx.jsonc +10 -0
- package/opencode/opencode.jsonc +103 -0
- package/opencode/package.json +25 -0
- package/opencode/patches/apply.sh +19 -0
- package/opencode/patches/opencode-pty-spawn.txt +49 -0
- package/opencode/plugin/background-agents.ts.disabled +483 -0
- package/opencode/plugin/kdco-primitives/get-project-id.ts +172 -0
- package/opencode/plugin/kdco-primitives/index.ts +26 -0
- package/opencode/plugin/kdco-primitives/log-warn.ts +51 -0
- package/opencode/plugin/kdco-primitives/mutex.ts +122 -0
- package/opencode/plugin/kdco-primitives/shell.ts +138 -0
- package/opencode/plugin/kdco-primitives/temp.ts +36 -0
- package/opencode/plugin/kdco-primitives/terminal-detect.ts +34 -0
- package/opencode/plugin/kdco-primitives/types.ts +13 -0
- package/opencode/plugin/kdco-primitives/with-timeout.ts +84 -0
- package/opencode/plugin/memory.ts +306 -0
- package/opencode/plugin/worktree/state.ts +412 -0
- package/opencode/plugin/worktree/terminal.ts +1002 -0
- package/opencode/plugin/worktree.ts +861 -0
- package/opencode/skills/KORTIX-browser/SKILL.md +478 -0
- package/opencode/skills/KORTIX-cron-triggers/SKILL.md +173 -0
- package/opencode/skills/KORTIX-deep-research/SKILL.md +278 -0
- package/opencode/skills/KORTIX-docx/SKILL.md +398 -0
- package/opencode/skills/KORTIX-docx/scripts/__init__.py +1 -0
- package/opencode/skills/KORTIX-docx/scripts/accept_changes.py +104 -0
- package/opencode/skills/KORTIX-docx/scripts/comment.py +244 -0
- package/opencode/skills/KORTIX-docx/scripts/office/helpers/__init__.py +0 -0
- package/opencode/skills/KORTIX-docx/scripts/office/helpers/merge_runs.py +199 -0
- package/opencode/skills/KORTIX-docx/scripts/office/helpers/simplify_redlines.py +197 -0
- package/opencode/skills/KORTIX-docx/scripts/office/pack.py +159 -0
- package/opencode/skills/KORTIX-docx/scripts/office/soffice.py +183 -0
- package/opencode/skills/KORTIX-docx/scripts/office/unpack.py +132 -0
- package/opencode/skills/KORTIX-docx/scripts/office/validate.py +111 -0
- package/opencode/skills/KORTIX-docx/scripts/office/validators/__init__.py +15 -0
- package/opencode/skills/KORTIX-docx/scripts/office/validators/base.py +847 -0
- package/opencode/skills/KORTIX-docx/scripts/office/validators/docx.py +446 -0
- package/opencode/skills/KORTIX-docx/scripts/office/validators/pptx.py +275 -0
- package/opencode/skills/KORTIX-docx/scripts/office/validators/redlining.py +247 -0
- package/opencode/skills/KORTIX-docx/scripts/render_docx.py +179 -0
- package/opencode/skills/KORTIX-docx/scripts/templates/comments.xml +3 -0
- package/opencode/skills/KORTIX-docx/scripts/templates/commentsExtended.xml +3 -0
- package/opencode/skills/KORTIX-docx/scripts/templates/commentsExtensible.xml +3 -0
- package/opencode/skills/KORTIX-docx/scripts/templates/commentsIds.xml +3 -0
- package/opencode/skills/KORTIX-docx/scripts/templates/people.xml +3 -0
- package/opencode/skills/KORTIX-domain-research/SKILL.md +96 -0
- package/opencode/skills/KORTIX-domain-research/scripts/domain-lookup.py +810 -0
- package/opencode/skills/KORTIX-elevenlabs/SKILL.md +230 -0
- package/opencode/skills/KORTIX-elevenlabs/scripts/tts.py +389 -0
- package/opencode/skills/KORTIX-email/SKILL.md +145 -0
- package/opencode/skills/KORTIX-legal-writer/SKILL.md +409 -0
- package/opencode/skills/KORTIX-legal-writer/references/bluebook.md +152 -0
- package/opencode/skills/KORTIX-legal-writer/references/document-types.md +416 -0
- package/opencode/skills/KORTIX-legal-writer/scripts/courtlistener.py +291 -0
- package/opencode/skills/KORTIX-legal-writer/scripts/ecfr_lookup.py +299 -0
- package/opencode/skills/KORTIX-legal-writer/scripts/verify-legal.py +507 -0
- package/opencode/skills/KORTIX-logo-creator/SKILL.md +293 -0
- package/opencode/skills/KORTIX-logo-creator/references/prompt-patterns.md +134 -0
- package/opencode/skills/KORTIX-logo-creator/scripts/compose_logo.py +406 -0
- package/opencode/skills/KORTIX-logo-creator/scripts/create_logo_sheet.py +258 -0
- package/opencode/skills/KORTIX-logo-creator/scripts/remove_bg.py +96 -0
- package/opencode/skills/KORTIX-memory/SKILL.md +261 -0
- package/opencode/skills/KORTIX-memory/scripts/export-sessions.py +409 -0
- package/opencode/skills/KORTIX-paper-creator/SKILL.md +549 -0
- package/opencode/skills/KORTIX-paper-creator/assets/template.tex +101 -0
- package/opencode/skills/KORTIX-paper-creator/scripts/compile.sh +177 -0
- package/opencode/skills/KORTIX-paper-creator/scripts/openalex_to_bibtex.py +220 -0
- package/opencode/skills/KORTIX-paper-creator/scripts/verify.sh +354 -0
- package/opencode/skills/KORTIX-paper-search/SKILL.md +418 -0
- package/opencode/skills/KORTIX-pdf/SKILL.md +232 -0
- package/opencode/skills/KORTIX-pdf/forms.md +36 -0
- package/opencode/skills/KORTIX-pdf/reference.md +105 -0
- package/opencode/skills/KORTIX-pdf/scripts/check_bounding_boxes.py +65 -0
- package/opencode/skills/KORTIX-pdf/scripts/check_fillable_fields.py +11 -0
- package/opencode/skills/KORTIX-pdf/scripts/convert_pdf_to_images.py +33 -0
- package/opencode/skills/KORTIX-pdf/scripts/create_validation_image.py +37 -0
- package/opencode/skills/KORTIX-pdf/scripts/extract_form_field_info.py +122 -0
- package/opencode/skills/KORTIX-pdf/scripts/extract_form_structure.py +115 -0
- package/opencode/skills/KORTIX-pdf/scripts/fill_fillable_fields.py +98 -0
- package/opencode/skills/KORTIX-pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
- package/opencode/skills/KORTIX-plan/SKILL.md +228 -0
- package/opencode/skills/KORTIX-presentation-viewer/SKILL.md +87 -0
- package/opencode/skills/KORTIX-presentation-viewer/serve.ts +136 -0
- package/opencode/skills/KORTIX-presentation-viewer/viewer.html +559 -0
- package/opencode/skills/KORTIX-presentations/SKILL.md +344 -0
- package/opencode/skills/KORTIX-remotion/SKILL.md +56 -0
- package/opencode/skills/KORTIX-remotion/rules/3d.md +86 -0
- package/opencode/skills/KORTIX-remotion/rules/animations.md +29 -0
- package/opencode/skills/KORTIX-remotion/rules/assets.md +78 -0
- package/opencode/skills/KORTIX-remotion/rules/audio-visualization.md +198 -0
- package/opencode/skills/KORTIX-remotion/rules/audio.md +169 -0
- package/opencode/skills/KORTIX-remotion/rules/calculate-metadata.md +104 -0
- package/opencode/skills/KORTIX-remotion/rules/can-decode.md +75 -0
- package/opencode/skills/KORTIX-remotion/rules/charts.md +120 -0
- package/opencode/skills/KORTIX-remotion/rules/compositions.md +141 -0
- package/opencode/skills/KORTIX-remotion/rules/display-captions.md +184 -0
- package/opencode/skills/KORTIX-remotion/rules/extract-frames.md +229 -0
- package/opencode/skills/KORTIX-remotion/rules/ffmpeg.md +38 -0
- package/opencode/skills/KORTIX-remotion/rules/fonts.md +152 -0
- package/opencode/skills/KORTIX-remotion/rules/get-audio-duration.md +58 -0
- package/opencode/skills/KORTIX-remotion/rules/get-video-dimensions.md +68 -0
- package/opencode/skills/KORTIX-remotion/rules/get-video-duration.md +58 -0
- package/opencode/skills/KORTIX-remotion/rules/gifs.md +141 -0
- package/opencode/skills/KORTIX-remotion/rules/images.md +130 -0
- package/opencode/skills/KORTIX-remotion/rules/import-srt-captions.md +69 -0
- package/opencode/skills/KORTIX-remotion/rules/light-leaks.md +73 -0
- package/opencode/skills/KORTIX-remotion/rules/lottie.md +68 -0
- package/opencode/skills/KORTIX-remotion/rules/maps.md +401 -0
- package/opencode/skills/KORTIX-remotion/rules/measuring-dom-nodes.md +35 -0
- package/opencode/skills/KORTIX-remotion/rules/measuring-text.md +143 -0
- package/opencode/skills/KORTIX-remotion/rules/parameters.md +98 -0
- package/opencode/skills/KORTIX-remotion/rules/sequencing.md +118 -0
- package/opencode/skills/KORTIX-remotion/rules/subtitles.md +36 -0
- package/opencode/skills/KORTIX-remotion/rules/tailwind.md +11 -0
- package/opencode/skills/KORTIX-remotion/rules/text-animations.md +20 -0
- package/opencode/skills/KORTIX-remotion/rules/timing.md +179 -0
- package/opencode/skills/KORTIX-remotion/rules/transcribe-captions.md +70 -0
- package/opencode/skills/KORTIX-remotion/rules/transitions.md +197 -0
- package/opencode/skills/KORTIX-remotion/rules/transparent-videos.md +106 -0
- package/opencode/skills/KORTIX-remotion/rules/trimming.md +53 -0
- package/opencode/skills/KORTIX-remotion/rules/videos.md +171 -0
- package/opencode/skills/KORTIX-secrets/SKILL.md +280 -0
- package/opencode/skills/KORTIX-semantic-search/SKILL.md +213 -0
- package/opencode/skills/KORTIX-session-search/SKILL.md +807 -0
- package/opencode/skills/KORTIX-session-search/Untitled +1 -0
- package/opencode/skills/KORTIX-skill-creator/SKILL.md +163 -0
- package/opencode/skills/KORTIX-web-research/SKILL.md +69 -0
- package/opencode/skills/KORTIX-xlsx/LICENSE.txt +30 -0
- package/opencode/skills/KORTIX-xlsx/SKILL.md +549 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/helpers/__init__.py +0 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/helpers/merge_runs.py +199 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/pack.py +159 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/soffice.py +183 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/unpack.py +132 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/validate.py +111 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/validators/__init__.py +15 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/validators/base.py +847 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/validators/docx.py +446 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/validators/pptx.py +275 -0
- package/opencode/skills/KORTIX-xlsx/scripts/office/validators/redlining.py +247 -0
- package/opencode/skills/KORTIX-xlsx/scripts/recalc.py +184 -0
- package/opencode/tools/image-gen.ts +342 -0
- package/opencode/tools/image-search.ts +190 -0
- package/opencode/tools/memory-get.ts +168 -0
- package/opencode/tools/memory-search.ts +247 -0
- package/opencode/tools/presentation-gen.ts +723 -0
- package/opencode/tools/scrape-webpage.ts +115 -0
- package/opencode/tools/scripts/.python-version +1 -0
- package/opencode/tools/scripts/convert_pdf.py +184 -0
- package/opencode/tools/scripts/convert_pptx.py +562 -0
- package/opencode/tools/scripts/pyproject.toml +11 -0
- package/opencode/tools/scripts/uv.lock +287 -0
- package/opencode/tools/scripts/validate_slide.py +74 -0
- package/opencode/tools/show-user.ts +217 -0
- package/opencode/tools/tests/e2e-presentation-fix.ts +277 -0
- package/opencode/tools/tests/image-gen.test.ts +215 -0
- package/opencode/tools/tests/image-search.test.ts +125 -0
- package/opencode/tools/tests/memory-system-benchmark.ts +1076 -0
- package/opencode/tools/tests/presentation-gen.test.ts +389 -0
- package/opencode/tools/tests/scrape-webpage.test.ts +74 -0
- package/opencode/tools/tests/show-user.test.ts +241 -0
- package/opencode/tools/tests/video-gen.test.ts +110 -0
- package/opencode/tools/tests/web-search.test.ts +106 -0
- package/opencode/tools/video-gen.ts +200 -0
- package/opencode/tools/web-search.ts +153 -0
- package/opencode/tsconfig.json +29 -0
- package/package.json +36 -0
- package/patch-agent-browser.js +100 -0
- package/postinstall.sh +88 -0
- package/services/KORTIX-presentation-viewer/run +37 -0
- package/services/agent-browser-viewer/run +48 -0
- package/services/kortix-master/run +16 -0
- package/services/lss-sync/run +22 -0
- package/services/opencode-serve/run +25 -0
- package/services/opencode-web/run +21 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: compositions
|
|
3
|
+
description: Defining compositions, stills, folders, default props and dynamic metadata
|
|
4
|
+
metadata:
|
|
5
|
+
tags: composition, still, folder, props, metadata
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
A `<Composition>` defines the component, width, height, fps and duration of a renderable video.
|
|
9
|
+
|
|
10
|
+
It normally is placed in the `src/Root.tsx` file.
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
import {Composition} from 'remotion';
|
|
14
|
+
import {MyComposition} from './MyComposition';
|
|
15
|
+
|
|
16
|
+
export const RemotionRoot = () => {
|
|
17
|
+
return <Composition id="MyComposition" component={MyComposition} durationInFrames={100} fps={30} width={1080} height={1080} />;
|
|
18
|
+
};
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Default Props
|
|
22
|
+
|
|
23
|
+
Pass `defaultProps` to provide initial values for your component.
|
|
24
|
+
Values must be JSON-serializable (`Date`, `Map`, `Set`, and `staticFile()` are supported).
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
import {Composition} from 'remotion';
|
|
28
|
+
import {MyComposition, MyCompositionProps} from './MyComposition';
|
|
29
|
+
|
|
30
|
+
export const RemotionRoot = () => {
|
|
31
|
+
return (
|
|
32
|
+
<Composition
|
|
33
|
+
id="MyComposition"
|
|
34
|
+
component={MyComposition}
|
|
35
|
+
durationInFrames={100}
|
|
36
|
+
fps={30}
|
|
37
|
+
width={1080}
|
|
38
|
+
height={1080}
|
|
39
|
+
defaultProps={
|
|
40
|
+
{
|
|
41
|
+
title: 'Hello World',
|
|
42
|
+
color: '#ff0000',
|
|
43
|
+
} satisfies MyCompositionProps
|
|
44
|
+
}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Use `type` declarations for props rather than `interface` to ensure `defaultProps` type safety.
|
|
51
|
+
|
|
52
|
+
## Folders
|
|
53
|
+
|
|
54
|
+
Use `<Folder>` to organize compositions in the sidebar.
|
|
55
|
+
Folder names can only contain letters, numbers, and hyphens.
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import {Composition, Folder} from 'remotion';
|
|
59
|
+
|
|
60
|
+
export const RemotionRoot = () => {
|
|
61
|
+
return (
|
|
62
|
+
<>
|
|
63
|
+
<Folder name="Marketing">
|
|
64
|
+
<Composition id="Promo" /* ... */ />
|
|
65
|
+
<Composition id="Ad" /* ... */ />
|
|
66
|
+
</Folder>
|
|
67
|
+
<Folder name="Social">
|
|
68
|
+
<Folder name="Instagram">
|
|
69
|
+
<Composition id="Story" /* ... */ />
|
|
70
|
+
<Composition id="Reel" /* ... */ />
|
|
71
|
+
</Folder>
|
|
72
|
+
</Folder>
|
|
73
|
+
</>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Stills
|
|
79
|
+
|
|
80
|
+
Use `<Still>` for single-frame images. It does not require `durationInFrames` or `fps`.
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import {Still} from 'remotion';
|
|
84
|
+
import {Thumbnail} from './Thumbnail';
|
|
85
|
+
|
|
86
|
+
export const RemotionRoot = () => {
|
|
87
|
+
return <Still id="Thumbnail" component={Thumbnail} width={1280} height={720} />;
|
|
88
|
+
};
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Calculate Metadata
|
|
92
|
+
|
|
93
|
+
Use `calculateMetadata` to make dimensions, duration, or props dynamic based on data.
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
import {Composition, CalculateMetadataFunction} from 'remotion';
|
|
97
|
+
import {MyComposition, MyCompositionProps} from './MyComposition';
|
|
98
|
+
|
|
99
|
+
const calculateMetadata: CalculateMetadataFunction<MyCompositionProps> = async ({props, abortSignal}) => {
|
|
100
|
+
const data = await fetch(`https://api.example.com/video/${props.videoId}`, {
|
|
101
|
+
signal: abortSignal,
|
|
102
|
+
}).then((res) => res.json());
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
durationInFrames: Math.ceil(data.duration * 30),
|
|
106
|
+
props: {
|
|
107
|
+
...props,
|
|
108
|
+
videoUrl: data.url,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const RemotionRoot = () => {
|
|
114
|
+
return (
|
|
115
|
+
<Composition
|
|
116
|
+
id="MyComposition"
|
|
117
|
+
component={MyComposition}
|
|
118
|
+
durationInFrames={100} // Placeholder, will be overridden
|
|
119
|
+
fps={30}
|
|
120
|
+
width={1080}
|
|
121
|
+
height={1080}
|
|
122
|
+
defaultProps={{videoId: 'abc123'}}
|
|
123
|
+
calculateMetadata={calculateMetadata}
|
|
124
|
+
/>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The function can return `props`, `durationInFrames`, `width`, `height`, `fps`, and codec-related defaults. It runs once before rendering begins.
|
|
130
|
+
|
|
131
|
+
## Nesting compositions within another
|
|
132
|
+
|
|
133
|
+
To add a composition within another composition, you can use the `<Sequence>` component with a `width` and `height` prop to specify the size of the composition.
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
<AbsoluteFill>
|
|
137
|
+
<Sequence width={COMPOSITION_WIDTH} height={COMPOSITION_HEIGHT}>
|
|
138
|
+
<CompositionComponent />
|
|
139
|
+
</Sequence>
|
|
140
|
+
</AbsoluteFill>
|
|
141
|
+
```
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: display-captions
|
|
3
|
+
description: Displaying captions in Remotion with TikTok-style pages and word highlighting
|
|
4
|
+
metadata:
|
|
5
|
+
tags: captions, subtitles, display, tiktok, highlight
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Displaying captions in Remotion
|
|
9
|
+
|
|
10
|
+
This guide explains how to display captions in Remotion, assuming you already have captions in the [`Caption`](https://www.remotion.dev/docs/captions/caption) format.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
Read [Transcribing audio](transcribe-captions.md) for how to generate captions.
|
|
15
|
+
|
|
16
|
+
First, the [`@remotion/captions`](https://www.remotion.dev/docs/captions) package needs to be installed.
|
|
17
|
+
If it is not installed, use the following command:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx remotion add @remotion/captions
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Fetching captions
|
|
24
|
+
|
|
25
|
+
First, fetch your captions JSON file. Use [`useDelayRender()`](https://www.remotion.dev/docs/use-delay-render) to hold the render until the captions are loaded:
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import { useState, useEffect, useCallback } from "react";
|
|
29
|
+
import { AbsoluteFill, staticFile, useDelayRender } from "remotion";
|
|
30
|
+
import type { Caption } from "@remotion/captions";
|
|
31
|
+
|
|
32
|
+
export const MyComponent: React.FC = () => {
|
|
33
|
+
const [captions, setCaptions] = useState<Caption[] | null>(null);
|
|
34
|
+
const { delayRender, continueRender, cancelRender } = useDelayRender();
|
|
35
|
+
const [handle] = useState(() => delayRender());
|
|
36
|
+
|
|
37
|
+
const fetchCaptions = useCallback(async () => {
|
|
38
|
+
try {
|
|
39
|
+
// Assuming captions.json is in the public/ folder.
|
|
40
|
+
const response = await fetch(staticFile("captions123.json"));
|
|
41
|
+
const data = await response.json();
|
|
42
|
+
setCaptions(data);
|
|
43
|
+
continueRender(handle);
|
|
44
|
+
} catch (e) {
|
|
45
|
+
cancelRender(e);
|
|
46
|
+
}
|
|
47
|
+
}, [continueRender, cancelRender, handle]);
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
fetchCaptions();
|
|
51
|
+
}, [fetchCaptions]);
|
|
52
|
+
|
|
53
|
+
if (!captions) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return <AbsoluteFill>{/* Render captions here */}</AbsoluteFill>;
|
|
58
|
+
};
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Creating pages
|
|
62
|
+
|
|
63
|
+
Use `createTikTokStyleCaptions()` to group captions into pages. The `combineTokensWithinMilliseconds` option controls how many words appear at once:
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { useMemo } from "react";
|
|
67
|
+
import { createTikTokStyleCaptions } from "@remotion/captions";
|
|
68
|
+
import type { Caption } from "@remotion/captions";
|
|
69
|
+
|
|
70
|
+
// How often captions should switch (in milliseconds)
|
|
71
|
+
// Higher values = more words per page
|
|
72
|
+
// Lower values = fewer words (more word-by-word)
|
|
73
|
+
const SWITCH_CAPTIONS_EVERY_MS = 1200;
|
|
74
|
+
|
|
75
|
+
const { pages } = useMemo(() => {
|
|
76
|
+
return createTikTokStyleCaptions({
|
|
77
|
+
captions,
|
|
78
|
+
combineTokensWithinMilliseconds: SWITCH_CAPTIONS_EVERY_MS,
|
|
79
|
+
});
|
|
80
|
+
}, [captions]);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Rendering with Sequences
|
|
84
|
+
|
|
85
|
+
Map over the pages and render each one in a `<Sequence>`. Calculate the start frame and duration from the page timing:
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { Sequence, useVideoConfig, AbsoluteFill } from "remotion";
|
|
89
|
+
import type { TikTokPage } from "@remotion/captions";
|
|
90
|
+
|
|
91
|
+
const CaptionedContent: React.FC = () => {
|
|
92
|
+
const { fps } = useVideoConfig();
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<AbsoluteFill>
|
|
96
|
+
{pages.map((page, index) => {
|
|
97
|
+
const nextPage = pages[index + 1] ?? null;
|
|
98
|
+
const startFrame = (page.startMs / 1000) * fps;
|
|
99
|
+
const endFrame = Math.min(
|
|
100
|
+
nextPage ? (nextPage.startMs / 1000) * fps : Infinity,
|
|
101
|
+
startFrame + (SWITCH_CAPTIONS_EVERY_MS / 1000) * fps,
|
|
102
|
+
);
|
|
103
|
+
const durationInFrames = endFrame - startFrame;
|
|
104
|
+
|
|
105
|
+
if (durationInFrames <= 0) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<Sequence
|
|
111
|
+
key={index}
|
|
112
|
+
from={startFrame}
|
|
113
|
+
durationInFrames={durationInFrames}
|
|
114
|
+
>
|
|
115
|
+
<CaptionPage page={page} />
|
|
116
|
+
</Sequence>
|
|
117
|
+
);
|
|
118
|
+
})}
|
|
119
|
+
</AbsoluteFill>
|
|
120
|
+
);
|
|
121
|
+
};
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## White-space preservation
|
|
125
|
+
|
|
126
|
+
The captions are whitespace sensitive. You should include spaces in the `text` field before each word. Use `whiteSpace: "pre"` to preserve the whitespace in the captions.
|
|
127
|
+
|
|
128
|
+
## Separate component for captions
|
|
129
|
+
|
|
130
|
+
Put captioning logic in a separate component.
|
|
131
|
+
Make a new file for it.
|
|
132
|
+
|
|
133
|
+
## Word highlighting
|
|
134
|
+
|
|
135
|
+
A caption page contains `tokens` which you can use to highlight the currently spoken word:
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
import { AbsoluteFill, useCurrentFrame, useVideoConfig } from "remotion";
|
|
139
|
+
import type { TikTokPage } from "@remotion/captions";
|
|
140
|
+
|
|
141
|
+
const HIGHLIGHT_COLOR = "#39E508";
|
|
142
|
+
|
|
143
|
+
const CaptionPage: React.FC<{ page: TikTokPage }> = ({ page }) => {
|
|
144
|
+
const frame = useCurrentFrame();
|
|
145
|
+
const { fps } = useVideoConfig();
|
|
146
|
+
|
|
147
|
+
// Current time relative to the start of the sequence
|
|
148
|
+
const currentTimeMs = (frame / fps) * 1000;
|
|
149
|
+
// Convert to absolute time by adding the page start
|
|
150
|
+
const absoluteTimeMs = page.startMs + currentTimeMs;
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<AbsoluteFill style={{ justifyContent: "center", alignItems: "center" }}>
|
|
154
|
+
<div style={{ fontSize: 80, fontWeight: "bold", whiteSpace: "pre" }}>
|
|
155
|
+
{page.tokens.map((token) => {
|
|
156
|
+
const isActive =
|
|
157
|
+
token.fromMs <= absoluteTimeMs && token.toMs > absoluteTimeMs;
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<span
|
|
161
|
+
key={token.fromMs}
|
|
162
|
+
style={{ color: isActive ? HIGHLIGHT_COLOR : "white" }}
|
|
163
|
+
>
|
|
164
|
+
{token.text}
|
|
165
|
+
</span>
|
|
166
|
+
);
|
|
167
|
+
})}
|
|
168
|
+
</div>
|
|
169
|
+
</AbsoluteFill>
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Display captions alongside video content
|
|
175
|
+
|
|
176
|
+
By default, put the captions alongside the video content, so the captions are in sync.
|
|
177
|
+
For each video, make a new captions JSON file.
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
<AbsoluteFill>
|
|
181
|
+
<Video src={staticFile("video.mp4")} />
|
|
182
|
+
<CaptionPage page={page} />
|
|
183
|
+
</AbsoluteFill>
|
|
184
|
+
```
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: extract-frames
|
|
3
|
+
description: Extract frames from videos at specific timestamps using Mediabunny
|
|
4
|
+
metadata:
|
|
5
|
+
tags: frames, extract, video, thumbnail, filmstrip, canvas
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Extracting frames from videos
|
|
9
|
+
|
|
10
|
+
Use Mediabunny to extract frames from videos at specific timestamps. This is useful for generating thumbnails, filmstrips, or processing individual frames.
|
|
11
|
+
|
|
12
|
+
## The `extractFrames()` function
|
|
13
|
+
|
|
14
|
+
This function can be copy-pasted into any project.
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
import {
|
|
18
|
+
ALL_FORMATS,
|
|
19
|
+
Input,
|
|
20
|
+
UrlSource,
|
|
21
|
+
VideoSample,
|
|
22
|
+
VideoSampleSink,
|
|
23
|
+
} from "mediabunny";
|
|
24
|
+
|
|
25
|
+
type Options = {
|
|
26
|
+
track: { width: number; height: number };
|
|
27
|
+
container: string;
|
|
28
|
+
durationInSeconds: number | null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type ExtractFramesTimestampsInSecondsFn = (
|
|
32
|
+
options: Options
|
|
33
|
+
) => Promise<number[]> | number[];
|
|
34
|
+
|
|
35
|
+
export type ExtractFramesProps = {
|
|
36
|
+
src: string;
|
|
37
|
+
timestampsInSeconds: number[] | ExtractFramesTimestampsInSecondsFn;
|
|
38
|
+
onVideoSample: (sample: VideoSample) => void;
|
|
39
|
+
signal?: AbortSignal;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export async function extractFrames({
|
|
43
|
+
src,
|
|
44
|
+
timestampsInSeconds,
|
|
45
|
+
onVideoSample,
|
|
46
|
+
signal,
|
|
47
|
+
}: ExtractFramesProps): Promise<void> {
|
|
48
|
+
using input = new Input({
|
|
49
|
+
formats: ALL_FORMATS,
|
|
50
|
+
source: new UrlSource(src),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const [durationInSeconds, format, videoTrack] = await Promise.all([
|
|
54
|
+
input.computeDuration(),
|
|
55
|
+
input.getFormat(),
|
|
56
|
+
input.getPrimaryVideoTrack(),
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
if (!videoTrack) {
|
|
60
|
+
throw new Error("No video track found in the input");
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (signal?.aborted) {
|
|
64
|
+
throw new Error("Aborted");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const timestamps =
|
|
68
|
+
typeof timestampsInSeconds === "function"
|
|
69
|
+
? await timestampsInSeconds({
|
|
70
|
+
track: {
|
|
71
|
+
width: videoTrack.displayWidth,
|
|
72
|
+
height: videoTrack.displayHeight,
|
|
73
|
+
},
|
|
74
|
+
container: format.name,
|
|
75
|
+
durationInSeconds,
|
|
76
|
+
})
|
|
77
|
+
: timestampsInSeconds;
|
|
78
|
+
|
|
79
|
+
if (timestamps.length === 0) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (signal?.aborted) {
|
|
84
|
+
throw new Error("Aborted");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const sink = new VideoSampleSink(videoTrack);
|
|
88
|
+
|
|
89
|
+
for await (using videoSample of sink.samplesAtTimestamps(timestamps)) {
|
|
90
|
+
if (signal?.aborted) {
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!videoSample) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
onVideoSample(videoSample);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Basic usage
|
|
104
|
+
|
|
105
|
+
Extract frames at specific timestamps:
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
await extractFrames({
|
|
109
|
+
src: "https://remotion.media/video.mp4",
|
|
110
|
+
timestampsInSeconds: [0, 1, 2, 3, 4],
|
|
111
|
+
onVideoSample: (sample) => {
|
|
112
|
+
const canvas = document.createElement("canvas");
|
|
113
|
+
canvas.width = sample.displayWidth;
|
|
114
|
+
canvas.height = sample.displayHeight;
|
|
115
|
+
const ctx = canvas.getContext("2d");
|
|
116
|
+
sample.draw(ctx!, 0, 0);
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Creating a filmstrip
|
|
122
|
+
|
|
123
|
+
Use a callback function to dynamically calculate timestamps based on video metadata:
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
const canvasWidth = 500;
|
|
127
|
+
const canvasHeight = 80;
|
|
128
|
+
const fromSeconds = 0;
|
|
129
|
+
const toSeconds = 10;
|
|
130
|
+
|
|
131
|
+
await extractFrames({
|
|
132
|
+
src: "https://remotion.media/video.mp4",
|
|
133
|
+
timestampsInSeconds: async ({ track, durationInSeconds }) => {
|
|
134
|
+
const aspectRatio = track.width / track.height;
|
|
135
|
+
const amountOfFramesFit = Math.ceil(
|
|
136
|
+
canvasWidth / (canvasHeight * aspectRatio)
|
|
137
|
+
);
|
|
138
|
+
const segmentDuration = toSeconds - fromSeconds;
|
|
139
|
+
const timestamps: number[] = [];
|
|
140
|
+
|
|
141
|
+
for (let i = 0; i < amountOfFramesFit; i++) {
|
|
142
|
+
timestamps.push(
|
|
143
|
+
fromSeconds + (segmentDuration / amountOfFramesFit) * (i + 0.5)
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return timestamps;
|
|
148
|
+
},
|
|
149
|
+
onVideoSample: (sample) => {
|
|
150
|
+
console.log(`Frame at ${sample.timestamp}s`);
|
|
151
|
+
|
|
152
|
+
const canvas = document.createElement("canvas");
|
|
153
|
+
canvas.width = sample.displayWidth;
|
|
154
|
+
canvas.height = sample.displayHeight;
|
|
155
|
+
const ctx = canvas.getContext("2d");
|
|
156
|
+
sample.draw(ctx!, 0, 0);
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Cancellation with AbortSignal
|
|
162
|
+
|
|
163
|
+
Cancel frame extraction after a timeout:
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
const controller = new AbortController();
|
|
167
|
+
|
|
168
|
+
setTimeout(() => controller.abort(), 5000);
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
await extractFrames({
|
|
172
|
+
src: "https://remotion.media/video.mp4",
|
|
173
|
+
timestampsInSeconds: [0, 1, 2, 3, 4],
|
|
174
|
+
onVideoSample: (sample) => {
|
|
175
|
+
using frame = sample;
|
|
176
|
+
const canvas = document.createElement("canvas");
|
|
177
|
+
canvas.width = frame.displayWidth;
|
|
178
|
+
canvas.height = frame.displayHeight;
|
|
179
|
+
const ctx = canvas.getContext("2d");
|
|
180
|
+
frame.draw(ctx!, 0, 0);
|
|
181
|
+
},
|
|
182
|
+
signal: controller.signal,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
console.log("Frame extraction complete!");
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error("Frame extraction was aborted or failed:", error);
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Timeout with Promise.race
|
|
192
|
+
|
|
193
|
+
```tsx
|
|
194
|
+
const controller = new AbortController();
|
|
195
|
+
|
|
196
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
197
|
+
const timeoutId = setTimeout(() => {
|
|
198
|
+
controller.abort();
|
|
199
|
+
reject(new Error("Frame extraction timed out after 10 seconds"));
|
|
200
|
+
}, 10000);
|
|
201
|
+
|
|
202
|
+
controller.signal.addEventListener("abort", () => clearTimeout(timeoutId), {
|
|
203
|
+
once: true,
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
await Promise.race([
|
|
209
|
+
extractFrames({
|
|
210
|
+
src: "https://remotion.media/video.mp4",
|
|
211
|
+
timestampsInSeconds: [0, 1, 2, 3, 4],
|
|
212
|
+
onVideoSample: (sample) => {
|
|
213
|
+
using frame = sample;
|
|
214
|
+
const canvas = document.createElement("canvas");
|
|
215
|
+
canvas.width = frame.displayWidth;
|
|
216
|
+
canvas.height = frame.displayHeight;
|
|
217
|
+
const ctx = canvas.getContext("2d");
|
|
218
|
+
frame.draw(ctx!, 0, 0);
|
|
219
|
+
},
|
|
220
|
+
signal: controller.signal,
|
|
221
|
+
}),
|
|
222
|
+
timeoutPromise,
|
|
223
|
+
]);
|
|
224
|
+
|
|
225
|
+
console.log("Frame extraction complete!");
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.error("Frame extraction was aborted or failed:", error);
|
|
228
|
+
}
|
|
229
|
+
```
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ffmpeg
|
|
3
|
+
description: Using FFmpeg and FFprobe in Remotion
|
|
4
|
+
metadata:
|
|
5
|
+
tags: ffmpeg, ffprobe, video, trimming
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## FFmpeg in Remotion
|
|
9
|
+
|
|
10
|
+
`ffmpeg` and `ffprobe` do not need to be installed. They are available via the `bunx remotion ffmpeg` and `bunx remotion ffprobe`:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
bunx remotion ffmpeg -i input.mp4 output.mp3
|
|
14
|
+
bunx remotion ffprobe input.mp4
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Trimming videos
|
|
18
|
+
|
|
19
|
+
You have 2 options for trimming videos:
|
|
20
|
+
|
|
21
|
+
1. Use the FFMpeg command line. You MUST re-encode the video to avoid frozen frames at the start of the video.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Re-encodes from the exact frame
|
|
25
|
+
bunx remotion ffmpeg -ss 00:00:05 -i public/input.mp4 -to 00:00:10 -c:v libx264 -c:a aac public/output.mp4
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
2. Use the `trimBefore` and `trimAfter` props of the `<Video>` component. The benefit is that this is non-destructive and you can change the trim at any time.
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { Video } from "@remotion/media";
|
|
32
|
+
|
|
33
|
+
<Video
|
|
34
|
+
src={staticFile("video.mp4")}
|
|
35
|
+
trimBefore={5 * fps}
|
|
36
|
+
trimAfter={10 * fps}
|
|
37
|
+
/>;
|
|
38
|
+
```
|