Media
A media object is one still, one clip, or one MP3 audio track, stored as exactly one rendition. There is no per-device fallback, so whatever media upload stores is what every screen has to decode.
Media folders organize the library. A folder ID starts mfd_ and a file ID med_. An upload lands at the library root unless --folder names a folder, media move and media folder update rearrange the tree, and a playlist by:folder selector plays the files directly inside one folder, not those of its subfolders. An organization holds at most 1000 folders across its projects, nested at most 10 levels deep, with names of 1 to 100 characters.
A media item keeps its id when its file changes: media replace swaps the file in place, so every playlist and schedule that uses it plays the new one, and media versions lists every file the item has had.
media upload#
Convert a local file to delivery bytes, upload it, and wait until the object is ready.
media upload <file> [--content-type TYPE] [--tag TAG] [--folder ID] [--no-wait] [--poll-ms MS]
[--timeout MS] [--no-transcode] [--codec h264|hevc] [--max-fps N]
[--max-edge PIXELS] [--webp-quality 1-100] [--no-progress]
[--preset signage-1080p30|signage-4k30] [--no-audio]| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<file> | path | yes | none | The source still, clip, or audio track. The filename becomes the human handle in listings, playlists, and playback reporting, so name it for what it shows. |
--content-type TYPE | string | no | the type of the file extension | Declare the source's media type for a file whose extension does not name it. A type that disagrees with the file's bytes is refused (usage_error, exit 2) before any conversion. |
--tag TAG | string | no | none | One to 32 letters or digits, matching ^[A-Za-z0-9]{1,32}$. Hyphens are rejected. A tag is what makes media list --tag TAG a reliable filename-to-id map. |
--folder ID | string | no | library root | An existing mfd_ media folder, checked before any work starts. The file lands there when it is ready, nothing moves afterwards, and data.folder_id reports it. It works with --no-wait. An uploader must name one of its folders; see Uploaders. |
--no-wait | boolean | no | off | Return as soon as the commit is accepted. The object is not ready yet; finish with operations wait. |
--poll-ms MS | integer | no | 1000 | Interval between operation polls while waiting. |
--timeout MS | integer | no | 120000 | Deadline for the wait. It also bounds each underlying request. |
--no-transcode | boolean | no | off | Upload the source bytes unchanged. ffmpeg, ffprobe, and cwebp are not run. The file must already be a PNG, JPEG, WebP, GIF, MP4, WebM or MP3, and lossless WebP is still rejected. Cannot combine with --codec, --max-fps, --max-edge, --webp-quality, --preset or --no-audio. |
--codec | string | no | h264 | h264 or hevc. Use hevc only when every screen that will play the clip is a native Player. |
--max-fps N | number | no | 30 | Constant output frame rate. Every video is encoded at exactly this rate whatever its source rate, so consecutive clips never change rate. Greater than 0 and at most 240. |
--max-edge PIXELS | integer | no | 3840 | Bound applied to each edge, from 16 to 3840. Aspect ratio is preserved and a smaller source is never upscaled. |
--webp-quality | integer | no | 90 | Still quality for the WebP encode, from 1 to 100. |
--preset | string | no | unset | signage-1080p30 or signage-4k30. Orientation-aware video size and a constant 30 fps. --max-edge and --max-fps can tighten a preset but cannot enlarge it. Video only; cannot combine with --no-transcode. |
--no-audio | boolean | no | off | Omit the audio track from video delivery. Video only; cannot combine with --no-transcode. |
--no-progress | boolean | no | off | Emit no progress output. Progress always goes to stderr; stdout stays reserved for the one result envelope. |
screenrig media upload ./lobby-welcome-loop.mp4 --tag LobbyLoopVideo becomes an MP4: H.264 High profile at level 4.2, 5.1 or 5.2, whichever is lowest for the size and rate, or H.265 Main profile at level 5.1 or 5.2 with --codec hevc, in yuv420p tagged Rec. 709, at most 3840 by 2160 pixels in a frame. An HDR source is tone mapped to Rec. 709. Audio, where the source has any, is AAC at 192 kbit/s, 48 kHz, stereo.
Stills become lossy WebP at quality 90, keeping alpha where the source has it. An animated source loops forever. A WebP that already fits the size bound is passed through rather than re-encoded.
Audio becomes an MP3 for a playlist soundtrack. An MP3 source uploads unchanged; WAV, AAC, M4A, OGG and FLAC sources are converted to a 128 kb/s MP3, which needs an ffmpeg build with libmp3lame. A track runs from 1 second to 3 hours. playlist init turns audio inputs into soundtrack tracks rather than pages. The ready object is an audio media object with a duration. It cannot be placed on a page; name it in the playlist audio soundtrack instead.
The ready object id is data.media_id. The same value is data.id and data.operation.result.media_id. That id is what a playlist selector takes.
Ready media with the same bytes, file name and tag is returned instead of uploading again (data.reused is true), so rerunning an interrupted upload returns its media ID. data.timing reports milliseconds per stage: prepare_ms (conversion and hash), declare_ms, transfer_ms, commit_ms and processing_ms (until ready, as fine as --poll-ms; absent with --no-wait).
A low-information filename such as video.mp4 adds an advisory generic_filename warning to the envelope. The upload still succeeds and the CLI never renames the file; pick the name before you run the command.
Conversion runs ffmpeg and ffprobe from PATH, or from the absolute paths in SCREENRIG_FFMPEG and SCREENRIG_FFPROBE. Stills prefer the ffmpeg libwebp encoder and fall back to cwebp on PATH or SCREENRIG_CWEBP. Run doctor before the first upload of a session.
--preset signage-1080p30 bounds the long edge to 1920, the short edge to 1080, and the rate to a constant 30 fps. signage-4k30 bounds the long edge to 3840, the short edge to 2160, and the rate to a constant 30 fps. Both work with landscape, portrait, and square content. --no-audio omits audio encoding for silent clips. These options apply only to video.
screenrig media upload ./lobby.mov --preset signage-1080p30 --no-audioscreenrig media upload ./menu.png --folder mfd_01EXAMPLEFOLDER000000000media upload-batch --folder puts every uploaded item in the folder the same way.
An image or video may be up to 16383 pixels wide or tall and 67,108,864 pixels (8192 by 8192) in one frame; a larger one is refused with invalid_request. The default conversion fits each edge within 3840 pixels, so upload an ultra-wide master such as 9216 by 2160 or 12000 by 540 with --no-transcode. One video upload may be at most the largest video upload of the project's plan, which project capabilities reports as media_video_bytes (see Pricing); a larger video is refused before it is read, so trim it to a shorter clip first. The CLI encodes video at a fixed quality, so a long video can still exceed the limit after encoding and is then refused; with --no-transcode the file itself must fit.
A transfer to storage that is cut off is tried up to 4 times while its upload URL is valid. If it still fails, the error names the upload's operation_id and the upload is cancelled so it holds no storage; with --idempotency-key it is kept for the rerun. Run the same command again.
Under Propose access, or when the project requires review for agent changes, a new upload waits for a person's review before a tag, folder or all selector plays it; see Reviews.
media upload-batch#
Upload many local files from a manifest, resuming from a state file.
media upload-batch <manifest.json> [--state FILE] [--concurrency N] [--tag TAG] [--folder ID]
[--poll-ms MS] [--timeout MS] [--no-transcode] [--codec h264|hevc]
[--max-fps N] [--max-edge PIXELS] [--webp-quality 1-100] [--no-progress]
[--preset signage-1080p30|signage-4k30] [--no-audio]| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<manifest.json> | path | yes | none | { "items": [ { "path": "./a.png", "tag"?: "Lobby", "content_type"?: "image/png" } ] } with 1 to 1000 items. Paths are relative to the manifest file. |
--state FILE | path | no | private file beside config | A 0600 JSON file keyed by the SHA-256 of each source file's local bytes; the default is one file per project, API address and manifest. Items already present with a media_id are reported as resumed. Do not share one state file across projects. |
--concurrency N | integer | no | 4 | Parallel uploads, 1 to 8. |
--no-transcode | boolean | no | off | Upload the source bytes unchanged, as media upload does. Cannot combine with --codec, --max-fps, --max-edge, --webp-quality, --preset or --no-audio. |
--poll-ms MS | integer | no | 1000 | Interval between operation polls while waiting. |
--timeout MS | integer | no | 120000 | Deadline for the wait. It also bounds each underlying request. |
--codec | string | no | h264 | h264 or hevc. Use hevc only when every screen that will play the clip is a native Player. |
--max-fps N | number | no | 30 | Constant output frame rate. Every video is encoded at exactly this rate whatever its source rate, so consecutive clips never change rate. Greater than 0 and at most 240. |
--max-edge PIXELS | integer | no | 3840 | Bound applied to each edge, from 16 to 3840. Aspect ratio is preserved and a smaller source is never upscaled. |
--webp-quality | integer | no | 90 | Still quality for the WebP encode, from 1 to 100. |
--preset | string | no | unset | signage-1080p30 or signage-4k30. Orientation-aware video size and a constant 30 fps. --max-edge and --max-fps can tighten a preset but cannot enlarge it. Video only; cannot combine with --no-transcode. |
--no-audio | boolean | no | off | Omit the audio track from video delivery. Video only; cannot combine with --no-transcode. |
--tag TAG | string | no | none | Applied when an item omits its own tag. |
--folder ID | string | no | library root | An existing mfd_ media folder, checked before any work starts. Every item lands there when it is ready, and data.folder_id reports it. An uploader must name one of its folders. |
--no-progress | boolean | no | off | Emit no progress output. |
screenrig media upload-batch ./images.json --state ./upload-state.jsonThe envelope carries data.items[] in manifest order, each with path, source_filename, sha256, outcome (accepted or resumed), and media_id. data.failed[] carries items that did not land, each with its path and problem; data.accepted, data.resumed, data.attempts, data.rate_limited, data.wait_ms and data.transfer_ms count the run. An item is tried up to 8 times, waiting between tries, while the answer is a rate limit or a server error.
With failures, the exit code follows the last failed item: 7 for a rate limit, 9 for a server error, 2 for a usage error, otherwise 8. Run the same command again with the same manifest to upload only what did not land.
media replace#
Replace the file of a media item in place. Its id, playlists and schedules keep working.
media replace <id> <file> [--expect-rev REVISION] [--note TEXT] [--content-type TYPE] [--no-wait]
[--poll-ms MS] [--timeout MS] [--no-transcode] [--codec h264|hevc] [--max-fps N]
[--max-edge PIXELS] [--webp-quality 1-100] [--no-progress]
[--preset signage-1080p30|signage-4k30] [--no-audio]| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<id> | string | yes | none | The med_ object id. |
<file> | path | yes | none | The new still, clip, or audio track. It must be the same kind as the media: image for image, video for video, audio for audio. Checked before any work starts (usage_error, exit 2). |
--expect-rev REVISION | string | no | none | Optional revision precondition. A supplied stale value is revision_conflict and the declared upload is cancelled: read media show ID and run the command again. |
--note TEXT | string | no | none | Kept with the new version and shown to a person who reviews it. At most 280 characters. |
--content-type TYPE | string | no | detected from the uploaded bytes | Declare the media type yourself instead of letting the CLI detect it. |
--no-wait | boolean | no | off | Return as soon as the replacement is accepted. The new file is not in service yet; finish with operations wait. |
--poll-ms MS | integer | no | 1000 | Interval between operation polls while waiting. |
--timeout MS | integer | no | 120000 | Deadline for the wait. It also bounds each underlying request. |
--no-transcode | boolean | no | off | Upload the source bytes unchanged. ffmpeg, ffprobe, and cwebp are not run. The file must already be a PNG, JPEG, WebP, GIF, MP4, WebM or MP3, and lossless WebP is still rejected. Cannot combine with --codec, --max-fps, --max-edge, --webp-quality, --preset or --no-audio. |
--codec | string | no | h264 | h264 or hevc. Use hevc only when every screen that will play the clip is a native Player. |
--max-fps N | number | no | 30 | Constant output frame rate. Every video is encoded at exactly this rate whatever its source rate, so consecutive clips never change rate. Greater than 0 and at most 240. |
--max-edge PIXELS | integer | no | 3840 | Bound applied to each edge, from 16 to 3840. Aspect ratio is preserved and a smaller source is never upscaled. |
--webp-quality | integer | no | 90 | Still quality for the WebP encode, from 1 to 100. |
--preset | string | no | unset | signage-1080p30 or signage-4k30. Orientation-aware video size and a constant 30 fps. --max-edge and --max-fps can tighten a preset but cannot enlarge it. Video only; cannot combine with --no-transcode. |
--no-audio | boolean | no | off | Omit the audio track from video delivery. Video only; cannot combine with --no-transcode. |
--no-progress | boolean | no | off | Emit no progress output. Progress always goes to stderr; stdout stays reserved for the one result envelope. |
screenrig media replace med_01EXAMPLEIMAGE0000000000 ./menu-v2.png
screenrig media replace med_01EXAMPLEVIDEO0000000000 ./intro.mp4 --expect-rev 3 --note "Spring cut"The media keeps its id, tags and folder, so every playlist and schedule that names it plays the new file at the next manifest, and tag, folder and all selectors pick it up too. Nothing is rescheduled. The command runs the same steps as media upload (conversion, the plan's video byte limit, the dimension limits, the plan's storage limit, the signed transfer) and then replaces instead of adding new media. It takes no --tag or --folder.
By default the command waits for processing. data.media_id and data.id are the media id, data.version is the number of the new file in media versions, and data.revision is the media's new revision. data.warnings, data.operation, data.upload, data.transcode and the timing are reported as for media upload, and the operation id is on the envelope. With --no-wait, data.operation is the accepted operation and data.version and data.warnings are absent.
A changed aspect ratio is allowed. The envelope carries an aspect_ratio_changed warning, and data.warnings[].playlists names the playlists that use the media, so check how they look. A file with the same bytes as the current one is refused with resource_conflict (exit 13).
Every earlier file stays in the media's history, and counts toward stored bytes until its version is deleted; see media versions.
Under Propose access, or when the project requires review for agent changes, the replacement waits for a person and the command succeeds (exit 0): data.status is pending_review, data.review_id names the review, warning pending_review gives its URL and data.next is screenrig reviews show ID. The media keeps playing its current file until a person approves, so tell them it is waiting and do not repeat the write. A newer replacement of the same media supersedes the one that waits. See Reviews.
media versions#
List the files a media item has had, the file in service first.
media versions <id> [--limit N] [--cursor CURSOR]| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<id> | string | yes | none | The med_ object id. |
--limit N | integer | no | every page | Read one page of this many versions, 1 to 100. |
--cursor CURSOR | string | no | none | Read the page after the data.next_cursor of the previous page. |
screenrig media versions med_01EXAMPLEIMAGE0000000000data.items has one entry per file the media has had: number, current, filename, source_filename, content_type, sha256, bytes, width, height, duration_ms, codecs, source (upload, replace, sync or restore), note, by (kind person, agent, client or sync, with id and name), restored_from and created_at. The file in service comes first (current: true), then the others newest first. Version 1 is the first upload; each replacement, restore or media sync change adds the next number.
Without --limit and --cursor every page is read and data.next_cursor is null. With either, one page is read: while data.next_cursor is a string, run the command again with --cursor set to it. Read only access is enough. A past version's bytes stay stored and count toward the project's stored bytes until media version delete drops them; there is no limit on how many versions are kept, and stored bytes above the plan's free storage are billed like any other storage.
media restore#
Put a past version back in service as a new version.
media restore <id> --number N [--expect-rev REVISION] [--note TEXT]| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<id> | string | yes | none | The med_ object id. |
--number N | integer | yes | none | The version number to put back, as media versions lists it. |
--expect-rev REVISION | string | no | none | Optional revision precondition. A supplied stale value is revision_conflict: read media show ID and run the command again. |
--note TEXT | string | no | none | Kept with the new version and shown to a person who reviews it. At most 280 characters. |
screenrig media restore med_01EXAMPLEIMAGE0000000000 --number 2
screenrig media restore med_01EXAMPLEIMAGE0000000000 --number 2 --expect-rev 5 --note "Back to the winter menu"The media keeps its id and moves to a new revision, and every playlist and schedule that names it plays that file again. The version it copies stays in the history and shares its bytes; the new version counts toward stored bytes like any other. data.media is the media and data.warnings carries aspect_ratio_changed, naming the playlists that use the media, when the restored file has another aspect ratio.
The version in service cannot be restored (resource_conflict, exit 5), and an unknown version is not_found (exit 4). Under Propose access, or when the project requires review for agent changes, the restore waits for a person and the command succeeds (exit 0): data.status is pending_review with data.review, warning pending_review gives its URL and data.next is screenrig reviews show ID. The media keeps playing its current file until a person approves, so do not repeat the write.
media version delete#
Delete a past version and its stored bytes.
media version delete <id> --number N| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<id> | string | yes | none | The med_ object id. |
--number N | integer | yes | none | The version number to delete, as media versions lists it. |
screenrig media version delete med_01EXAMPLEIMAGE0000000000 --number 2The version's record goes and its bytes leave the project's stored bytes at once. The stored object is removed after 24 hours unless another version or the file in service holds the same file. A deleted version cannot be restored. The version in service cannot be deleted, and no version can be deleted while a replacement of the media waits for review; both are resource_conflict (exit 5), and media versions ID shows which version is current. An unknown version is not_found (exit 4). Deleting needs Manage access: a Propose credential is refused with insufficient_access, and the delete is never proposed for review.
media show#
Read one media object, including its current revision.
media show <id>| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<id> | string | yes | none | The med_ object id. |
screenrig media show med_01EXAMPLEIMAGE0000000000media download#
Write a stored rendition to disk for inspection. Never print the bytes.
media download <id> [--output FILE]| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<id> | string | yes | none | The med_ object id. |
--output FILE | path | no | ./<id>.<ext> in the current directory | A file path, not a directory. An existing file is overwritten. The extension follows the content type. |
screenrig media download med_01EXAMPLEIMAGE0000000000 --output ./lobby.webpThe envelope reports media_id, path, bytes, sha256, content_type, primitive, filename, optional source_filename, and width / height. It never carries pixels. Use this to proof an image before it goes on glass.
media list#
List media objects, optionally filtered by tag, primitive and folder.
media list [--tag TAG] [--primitive image|video|audio] [--folder ID|root] [--library]| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
--tag TAG | string | no | none | Match ^[A-Za-z0-9]{1,32}$. Untagged objects are omitted whenever this filter is present. |
--primitive | string | no | all | image, video, or audio. Restrict the listing to stills, clips, or soundtrack tracks. |
--folder ID|root | string | no | every folder | Only files directly inside this mfd_ folder, not its subfolders, or with root only files at the library root. The API applies the filter. |
--library | boolean | no | off | List the organization Library's media instead, read-only: rows carry library: true. Use their IDs in playlist pages; change them only in the Library project. |
screenrig media list --tag LobbyLoop --primitive video
screenrig media list --folder mfd_01EXAMPLEFOLDER000000000
screenrig media list --libraryEach row carries folder_id, which is absent at the library root.
media selector-preview#
Preview which ready files a dynamic media selector matches now, without publishing a playlist or advancing a Player.
media selector-preview <selector.json> --primitive image|video [--rect WxH] [--timezone IANA]| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<selector.json> | path | yes | none | A JSON file holding one all, tag or folder media selector, or - for stdin. A tag with a screen token cannot be previewed. |
--primitive | string | yes | none | image or video: the kind of file the selector matches. |
--rect WxH | string | with fit: "aspect" | none | The size of the primitive's rect, such as 1080x1920, both numbers above 0. |
--timezone IANA | string | no | UTC | The time zone the files' availability rules are read in, such as America/New_York. |
screenrig media selector-preview selector.json --primitive video
screenrig media selector-preview selector.json --primitive image --timezone America/New_YorkThe selector keys (order, batch_size, one_at_a_time, weights and fit) are described under Tag filters and playback batches and Weighted rotation and aspect fit.
The answer carries matched_count, the filename-ordered candidates (media_id, filename, tags, width, height), the normalized selector, and in excluded the files left out with their reason: aspect_mismatch for fit: "aspect", and outside_window for a file whose availability rule does not hold now. With order: "weighted" each candidate carries weight and share_percent, and cycle is the number of plays after which every item has played exactly its weight. More than 1024 matches is invalid_request. Read access is enough. The preview reads the authenticated project, never advances a cursor or changes a manifest, and a catalog change after it is picked up at each Player's next loop boundary.
media update#
Replace or clear the tags of one object. Its availability rule is set with media availability.
media update <id> (--tag TAG | --tags TAG1,TAG2 | --clear-tag) [--expect-rev REVISION]| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<id> | string | yes | none | The med_ object id. |
--tag TAG | string | one of the three | none | The replacement tag, one to 32 letters or digits. It becomes the object's only tag. |
--tags TAG1,TAG2 | string | one of the three | none | The replacement tag set: 1 to 32 unique tags separated by commas, each one to 32 letters or digits. |
--clear-tag | boolean | one of the three | off | Remove every tag and leave the object untagged. |
--expect-rev REVISION | string | no | none | Optional revision precondition. Omit to write the current resource. A supplied stale value is revision_conflict: refetch, reapply, retry. |
screenrig media update med_01EXAMPLEIMAGE0000000000 --tag LobbyLoop
screenrig media update med_01EXAMPLEIMAGE0000000000 --tags Summer,DrinksUnder Propose access, or when the project requires review for agent changes, retagging approved media answers ok with warning media_review_pending: screens keep playing it with its approved tags until a person approves the change, and a rejection puts the tags back. Its review appears in reviews list within a few seconds; a retag that changes no approved playlist and no playlist waiting for review applies on its own. See Reviews.
media availability#
Set or clear when dynamic selectors may pick one file.
media availability <id> (--file FILE | --clear) [--expect-rev REVISION]| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<id> | string | yes | none | The med_ object id. |
--file FILE | path | one of the two | none | A JSON availability rule, or - for stdin. |
--clear | boolean | one of the two | off | Remove the rule; the file is always available. |
--expect-rev REVISION | string | no | current revision | Optional revision precondition. Omitted, the command reads the media's current revision first. A supplied stale value is revision_conflict: refetch, reapply, retry. |
screenrig media availability med_01EXAMPLEIMAGE0000000000 --file rule.jsonThe rule's members and behavior are under Media availability. A file waiting for a person's review is refused with media_pending_review: decide its review first.
media delete#
Delete one media object.
media delete <id> [--expect-rev REVISION]| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<id> | string | yes | none | The med_ object id. |
--expect-rev REVISION | string | no | none | Optional revision precondition. Omit to write the current resource. A supplied stale value is revision_conflict: refetch, reapply, retry. |
screenrig media delete med_01EXAMPLEIMAGE0000000000media move#
Move media files and folders into a folder, or back to the library root.
media move <id>... (--folder ID | --root)| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<id>... | string | yes | none | One or more med_ file IDs and mfd_ folder IDs, at most 1000 in all. |
--folder ID | string | one of the two | none | The destination mfd_ folder. |
--root | boolean | one of the two | off | Move to the library root. |
screenrig media move med_01EXAMPLEIMAGE0000000000 med_01EXAMPLEVIDEO0000000000 --folder mfd_01EXAMPLEFOLDER000000000
screenrig media move mfd_01EXAMPLEFOLDER000000000 --rootThe move is one all-or-nothing request. A folder cannot move into itself or a folder inside it, and folders nest at most 10 levels deep. Items already in the destination stay as they are. data.media and data.folders count what moved.
Moving files changes what the by:folder selectors of their old and new folders play, from each screen's next selector resolve; moving a folder changes no folder's own files. Under Propose access, or when the project requires review for agent changes, each moved file waits for a person's review (reviews list) and approved playlists keep playing it by the folder it was approved with; see Reviews.
media folder list#
List the project's media folders.
media folder list [--library]| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
--library | boolean | no | off | List the organization Library's folders instead, read-only: rows carry library: true, and a by:folder selector may name one to play its files. |
screenrig media folder list
screenrig media folder list --libraryReturns every folder of the project, at most 1000, ordered by name: data.items with id, name, parent_id (null at the library root) and revision. Build the tree from parent_id. A file's folder is folder_id on media list and media show; it is absent at the library root.
media folder show#
Read one media folder.
media folder show <id>| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<id> | string | yes | none | The mfd_ folder id. |
screenrig media folder show mfd_01EXAMPLEFOLDER000000000The envelope carries the folder's name, parent_id and revision.
media folder create#
Create a media folder at the library root or inside another folder.
media folder create --name NAME [--parent ID]| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
--name NAME | string | yes | none | 1 to 100 characters, trimmed. Names may repeat. |
--parent ID | string | no | library root | The mfd_ folder to create it in. |
screenrig media folder create --name Menus
screenrig media folder create --name Lunch --parent mfd_01EXAMPLEFOLDER000000000An organization holds at most 1000 folders across its projects, nested at most 10 levels deep. A playlist plays a folder's files with a by:folder selector.
media folder update#
Rename a media folder, or move it with everything inside into another folder.
media folder update <id> [--name NAME] [--parent ID | --root] [--expect-rev REVISION]| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<id> | string | yes | none | The mfd_ folder id. |
--name NAME | string | at least one of --name, --parent, --root | unchanged | The new name, 1 to 100 characters. |
--parent ID | string | at least one of --name, --parent, --root | unchanged | Move the folder, with everything inside, into this mfd_ folder. Not with --root. |
--root | boolean | at least one of --name, --parent, --root | off | Move the folder to the library root. Not with --parent. |
--expect-rev REVISION | string | no | none | Optional revision precondition. Omit to write the current resource. A supplied stale value is revision_conflict: refetch, reapply, retry. |
screenrig media folder update mfd_01EXAMPLEFOLDER000000000 --name Archive
screenrig media folder update mfd_01EXAMPLEFOLDER000000000 --rootA folder cannot move into itself or a folder inside it, and a move must keep every folder within 10 levels. Sending the current values is a no-op.
media folder delete#
Delete a media folder. Its files and folders move up one level.
media folder delete <id> [--expect-rev REVISION]| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
<id> | string | yes | none | The mfd_ folder id. |
--expect-rev REVISION | string | no | none | Optional revision precondition. Omit to write the current resource. A supplied stale value is revision_conflict: refetch, reapply, retry. |
screenrig media folder delete mfd_01EXAMPLEFOLDER000000000Deletes only the folder. Its files and folders move up into its parent, or into the library root when it has none. A by:folder selector that names it then matches nothing, so repoint those playlists first.
Tag filters and playback batches
Media carries up to 32 unique tags, each 1–32 letters or digits. Replace the whole set with screenrig media update med_ID --tags Summer,Drinks. --tag Summer replaces it with one tag; --clear-tag clears every tag. media list --tag Summer matches membership anywhere in the set. Tags are case-sensitive and are never authorization.
A dynamic selector can use one tag or a tags filter object:
{
"by": "tag",
"tags": { "all": ["Summer", "Drinks"], "any": ["Lobby", "Patio"], "none": ["Expired"] },
"order": "random",
"one_at_a_time": false,
"batch_size": 3
}
all requires every listed tag, any requires at least one, and none excludes any listed tag. Present groups combine with AND; omitted groups impose no condition. Supply at least one nonempty group. Each group accepts up to 32 unique tags. tag and tags are mutually exclusive. A none-only filter selects all ready media of that primitive except the excluded tags. Image selectors select images and video selectors select videos.
A folder selector, { "by": "folder", "folder_id": "mfd_…" }, plays the ready files directly inside one media folder and none of its subfolders; media folder list shows the ids. It takes the same order, one_at_a_time and batch_size as all and tag, and deleting a folder moves its files up a level and leaves the selector empty.
The candidate pool contains up to 1,024 ready media per selector, with 1,024 media references across the complete loop snapshot. A write counts each all, tag or folder selector as 32 of those references and an ids selector as its length. A larger match returns an error; narrow the filters. batch_size is a separate limit of 1–1,024 items per page visit. On a media_end page, one_at_a_time: false plays the next batch in sequence; omitting batch_size plays the remaining pass. one_at_a_time: true plays one item per appearance and allows only an omitted batch size or batch_size: 1. On duration, application and audio_end pages every all, tag, folder and multi-id ids selector requires one_at_a_time: true, and dwell_ms is refused. Images on media_end also require dwell_ms; videos cannot loop on that page.
The Player resolves every dynamic media primitive in one request at playlist startup and at each loop boundary. Membership is frozen during the loop. Uploads, removals, tagging changes, and availability edges do not regenerate the manifest. At the next refresh, removed or newly excluded items leave the pending queue; new matches join when the current pass finishes. A final batch may contain fewer than the requested number; it does not refill from the next pass. Filename order sorts by filename, then media ID. Random order shuffles once per complete pass, preserving the remaining order across loop refreshes. Weighted order follows its weights, below. The cursor is local to the screen, playlist, page, and primitive, persists across restart, and resets when the filter, order, or batch policy changes. Offline playback uses the last successful snapshot. Empty matches skip the primitive and any resulting empty page.
Save the selector object above as selector.json, then inspect its current candidate catalog before publishing:
screenrig media selector-preview selector.json --primitive video
Preview returns matched_count, filename-ordered candidates with media IDs and tags, and the normalized selector policy. With random order it shows the pool and shuffle policy; it does not choose the Player's shuffle, advance a cursor, create a playback grant, or change a manifest. With weighted order it returns each candidate's weight and share_percent and the cycle. --rect WxH (API rect: {width, height}) is the slot's size and is required with fit: "aspect"; the matches it leaves out are listed in excluded with reason aspect_mismatch. Image selectors pass --primitive image. The API is POST /api/selectors/preview with { "primitive": "video", "selector": ... }. It uses the authenticated project and the same matching rules as playback. Catalog changes after preview are resolved at playback's next loop boundary.
Static bundle export freezes dynamic matches as exact IDs and shuffles a random selector once. It preserves one-at-a-time or complete-pass playback; a bounded runtime sequence using batch_size with one_at_a_time: false requires removing that field or choosing explicit IDs before export. This prevents silently losing a runtime batch cursor in a static bundle.
Weighted rotation and aspect fit
order: "weighted" rotates one slot's creatives by weight, so each sponsor keeps its share of plays. weights maps a media ID (med_...) or tag:NAME to an integer from 1 to 1,000, with at most 64 keys. It works with by of all, tag or folder:
{
"by": "all",
"order": "weighted",
"one_at_a_time": true,
"weights": { "tag:Acme": 50, "tag:Globex": 30, "tag:Initech": 20 },
"fit": "aspect"
}
Weights of 50, 30 and 20 deliver exactly 5, 3 and 2 of every 10 plays, every cycle, and a restart resumes mid-cycle. An item's weight is its media ID key when present; otherwise the first tag:NAME key, alphabetically, among the tags it carries, split equally among the items of the selection that carry it, so a sponsor's share holds however many creatives it has; otherwise the item counts as its own group with weight 1. A cycle is at most 4,096 plays; shares are rounded to it only when the exact ratio needs a longer one.
Weighted order plays one item per visit, so set one_at_a_time: true or batch_size: 1. weights is required with weighted order and allowed only with it; otherwise the write returns invalid_request. Weighted rotation is a Plus feature: without it, a write that sets a weight an item does not already hold answers feature_not_in_plan, while equal rotation, keeping stored weights and clearing them stay open. Every
Player plays weighted order. A Player that does not declare the
selector.weighted host capability is sent random, and the manifest
diagnostics carry selector_order_downgraded.
fit: "aspect" on a dynamic selector keeps only files whose width over height is within 5% of the slot's rectangle, a video wall or display frame included, read when the selector resolves. A portrait slot never plays a landscape file. No Player change is involved.
screenrig media selector-preview prints the expected share of every file of a weighted
selector and, with fit: aspect, lists the files it leaves out; the dashboard page editor shows the same table in
the inspector. Static bundle export freezes a weighted selector as exact IDs in filename order; weights are not kept.
Screen tokens in tag selectors
A tag selector's tag, and each entry of its tags filter, may be {screen.field.NAME} or {screen.id}, the screen's identifier without its scr_ prefix, which is a tag. A token may stand beside letters and digits in a template of up to 128 characters. The server fills the token for each screen when it builds that screen's manifest and selector snapshot, so one playlist plays each screen's own media. Tag the media Lobby or Garage, set each screen's field with screenrig screen update scr_ID --field zone=Lobby, and give the slot this selector:
{ "by": "tag", "tag": "{screen.field.zone}", "one_at_a_time": true }
The filled value follows the tag rules: 1 to 32 letters or digits. A screen without the field, or whose value is not a tag, selects nothing: it plays no file from that slot, and the manifest diagnostics carry screen_fact_missing or screen_fact_invalid. Changing a field re-mints that screen's manifest. A selector with a token cannot be previewed (media selector-preview takes a concrete tag) or frozen by playlist export.
Screen tokens in iframe addresses and application params
An iframe's src and fallback_src may carry screen tokens in the path, query or fragment of an http or https address, and so may the optional params of an application primitive: up to 16 names of 1 to 32 lower case letters, digits or underscores (ticket and policy are reserved) with values of up to 512 characters. The server fills them for each screen:
| Token | Value |
|---|---|
{screen.id} | The screen's identifier without its scr_ prefix. |
{screen.name} | The screen's name. |
{screen.timezone} | The screen's effective IANA timezone: its override, else the zone its Player reports, else UTC. |
{screen.field.NAME} | The screen's field NAME, 1 to 32 lower case letters, digits or underscores. |
{screen.location.lat}, {screen.location.lon} | Its location in decimal degrees. |
{screen.location.name}, {screen.location.address} | Its place name and street address. |
{
"primitive": "iframe",
"src": "https://maps.example.com/directory?kiosk={screen.field.kiosk_id}&level={screen.field.level}",
"title": "Directory"
}
The write stores the template as written, braces included. Each screen's manifest carries its own address, every value percent-encoded for its place in the URL: a kiosk_id of B 2&3 becomes B%202%263. A token in the scheme, credentials, host or port, or in a YouTube link, a Google or Microsoft document link or a BI view address, is invalid_request at src (or fallback_src). Publishing skips the framing check for a templated src, and previews show the template as written.
A screen that lacks a value (an unset field, no location), or whose values fill into an address over 2048 characters or one with a path segment of . or .., gets no such iframe or application, and its manifest diagnostics carry screen_fact_missing or screen_fact_invalid; a page left empty is dropped. Every write that reaches such a screen (a playlist write, screen assign, screen update, a fleet assign, an approved review) answers with a screen_fact_missing warning that names the tokens, the first 20 screen ids and how many there are. Changing a field or location re-mints the manifest of the screens it affects.
For an application, the filled params, at most 4096 characters together once percent-encoded, reach it in context.params on every Player (SDK). The Player web app and the Players that run it (Samsung Tizen, BrightSign, LG webOS and Vega) also append each one to the launch address after ticket, percent-encoded, so the application can read location.search. A template's url field may be a whole iframe src, and its value may itself carry screen tokens, filled when the manifest is built. A tag selector uses {screen.id} and {screen.field.NAME} the same way (above). Cards keep their own {screen.…} tokens.
Application primitives in one playlist that pin the same release carry the same params.
Media availability
screenrig media availability med_ID --file rule.json sets when the dynamic selectors (all, tag and folder) may pick a file. --clear removes the rule and --expect-rev N guards the media revision. The API is PATCH /api/media/{id}/availability with If-Match and { "availability": {...} | null }. Setting a rule needs Manage access; a Propose credential gets insufficient_access. Without --expect-rev the command reads the media's current revision first. A file waiting for a person's review is refused with media_pending_review.
The rule is the schedule's clock grammar, read in each screen's time zone, and holds at least one member: from and until (civil YYYY-MM-DDTHH:MM, until exclusive), windows (weekly {days, start, end}), monthly ({day} or {nth, weekday}, each with optional start and end), dates ({date, start, end}) and except (dates that remove an occurrence, overnight tails included), with up to 16 windows, 16 monthly entries, 100 dates and 100 except dates. It has no data or interaction. A sponsor's creative for the long weekend, available only on weekday evenings inside the span:
{
"from": "2026-11-27T00:00",
"until": "2026-11-30T23:59",
"windows": [{ "days": ["mon", "tue", "wed", "thu", "fri"], "start": "17:00", "end": "22:00" }]
}
Only selectors read the rule: an explicit id or ids selector ignores it, as do cards' media references, soundtrack audio and ad creatives. A file leaves the selection and returns at each screen's next playlist loop, within about a minute of the edge. No playlist or manifest revision changes, and an offline Player keeps its last selection until it reconnects. media selector-preview with
--timezone ZONE (default UTC) reads the rules in that zone and lists the files
outside their rule now in excluded with reason outside_window.