Record with a template
A template recording renders a React template directly into an MP4 file. The template describes what the recording shows, and the recording is the only thing it produces: there is no output to register and no live stream to publish to.
The template runs inside a composition, which holds the inputs it renders. The Recordings article explains how template recordings relate to compositions and to recordings of an existing output.
Prerequisites​
- A running composition with inputs. Either register inputs yourself, as in Steps 1 and 2 of the compositions tutorial, or forward a Fishjam room into it and start the composition, as in Steps 4 and 5 of Compose a Fishjam room. A composition created with
autostart: falserenders nothing until it is started. You do not need a livestream or any other output. - A template project scaffolded with the composition CLI (see Write and deploy a template).
export FISHJAM_URL="https://fishjam.io/api/v1/connect/<YOUR_FISHJAM_ID>" export TOKEN="<YOUR_MANAGEMENT_TOKEN>" export COMPOSITION="<COMPOSITION_ID>"
Every recording call is a method on the FishjamClient of the JS and Python server SDKs, shown in the language tabs below.
Step 1: Write and bundle the template​
A recording template is written exactly like the template of an output. This one tiles every input that is playing, so the recording follows inputs as they come and go:
import {InputStream ,Rescaler ,Tiles ,View ,useInputStreams , } from "@swmansion/smelter"; export default functionApp () { constinputs =Object .values (useInputStreams ()); constplaying =inputs .filter ((input ) =>input .videoState === "playing"); return ( <View style ={{backgroundColor : "#0b1020ff" }}> <Tiles style ={{padding : 8 }}> {playing .map ((input ) => ( <Rescaler key ={input .inputId }> <InputStream inputId ={input .inputId } /> </Rescaler > ))} </Tiles > </View > ); }
The template decides everything the recording shows. It can use the room hooks from @fishjam-cloud/composition, such as usePeers(), to render a forwarded room (see Compose a Fishjam room), and the eventBus to react to events you send to the composition.
Build the bundle:
- npm
- Yarn
- pnpm
- Bun
npm run build
yarn build
pnpm run build
bun run build
The bundle, dist/App.js, can weigh at most 1 MiB.
Step 2: Start the recording​
Pass the composition in source and the bundle alongside it:
- Typescript
- Python
- curl
constrecording = awaitfishjamClient .createTemplateRecording ( {source : {compositionURL : "https://rtc.fishjam.io/api/composition/<COMPOSITION_ID>",resolution : {width : 1280,height : 720 }, },metadata : {show : "weekly-standup" }, }, "dist/App.js", );
The template is either a path to the bundle or a Blob with its contents. The response returns the recording with status active; capture has already started.
import os from fishjam import FishjamClient from fishjam.recording import TemplateSource, TemplateSourceResolution fishjam_client = FishjamClient( fishjam_id=os.environ["FISHJAM_ID"], management_token=os.environ["MANAGEMENT_TOKEN"], ) recording = fishjam_client.create_template_recording( source=TemplateSource( composition_url="https://rtc.fishjam.io/api/composition/<COMPOSITION_ID>", resolution=TemplateSourceResolution(width=1280, height=720), ), template="dist/App.js", metadata={"show": "weekly-standup"}, )
The template is either a path to the bundle or its contents as bytes. The response returns the recording with status active; capture has already started.
A template recording is created with a multipart/form-data request: the configuration in a config part and the bundle in a template part.
CONFIG=$(cat <<EOF { "source": { "compositionURL": "https://rtc.fishjam.io/api/composition/$COMPOSITION", "resolution": { "width": 1280, "height": 720 } }, "metadata": { "show": "weekly-standup" } } EOF ) curl -X POST "$FISHJAM_URL/recordings" \ -H "Authorization: Bearer $TOKEN" \ -F "config=$CONFIG;type=application/json" \ -F "template=@dist/App.js"
The response returns the recording with status active; capture has already started. Save its id from data.id:
export RECORDING="<RECORDING_ID>"
Note the following:
resolutionsets the size the template is rendered at. It defaults to 1280x720, has to be even on both sides, and can be at most 3840x2160.audiocontrols whether the recording captures audio. It defaults totrue; set it tofalsefor a video-only file.- A composition can run several template recordings at once, each with its own template.
metadatais optional and free-form. It is returned with the recording and can be used to filter recordings later.
Step 3: Stop and download the recording​
A template recording captures until you stop it or its composition ends. A composition ends when you delete it, or when it cleans itself up after five minutes without input media, so keep inputs flowing for as long as you want to record.
Stopping, checking the status, downloading the MP4, and deleting the recording work the same for every recording. Continue with Manage recordings.