Skip to main content

Your own art on a Samsung Frame.

curator is a web app you run yourself for the art on your Frame TVs. Upload pictures, choose their mattes, gather them into albums the TV rotates through, and back the lot up, all over the TV’s own local protocol.

MIT licensed. One Docker image beside a Postgres, with an Unraid template. Not made or endorsed by Samsung.

shadowbox_polar is what curator asks the TV for.

Everything the TV can do with art, from a browser

Find and pair
Search the network for Frames, or type an address, and accept the prompt on the TV once. Several TVs, one picker.
Upload
Drop a JPEG or PNG, or a WebP, AVIF, GIF or TIFF that curator converts. Pick its mattes and show it at once.
Library
Everything on the TV by category, as thumbnails or a list. Show a piece, change its matte, replace its image, or delete it.
Albums that rotate
Gather pieces into Halloween or Christmas, then have the TV cycle through one, in order or shuffled, as often as every minute.
Backups
A zip of every original, matte, album and rotation. Restore it onto the same TV or a new one, as often as you like.
Nothing in between
No phone app, no Samsung account and no cloud. curator talks to the TV on your network and keeps its data in your Postgres.

It talks to the TV, and checks what the TV did

A Frame answers art-mode requests on a WebSocket on your network. curator speaks that protocol from TypeScript, ported from samsung-tv-ws-api and matched to it byte for byte.

The TV rarely says yes. Most writes go unanswered, so curator reads the TV’s state back after a change instead of trusting the request. Uploads are the exception: they wait for the TV to announce the new piece.

One request at a time. The TV handles concurrent art requests badly, so each TV gets one connection that queues them, and thumbnails are fetched in batches and cached.

The Frame client

Upload, with a matte
curl -X POST "localhost:14100/api/tvs/$TV_ID/art?matte=shadowbox_polar" \
-H 'content-type: image/jpeg' \
--data-binary @photo.jpg

{ "contentId": "MY-F0042" }

Run it

The image, maroonedsoftware/curator on Docker Hub, holds the web app and the API, and applies its database migrations every time it starts. Point it at a Postgres 17 and it creates its database on the first start.

Searching for TVs needs multicast on their network, so run it on host networking to use it, or add each TV by address. On Unraid, the template does that for you.

The quick start has the whole compose file, and the Unraid steps.

compose.yaml, abridged
services:
curator:
image: maroonedsoftware/curator:latest
ports: ['14100:14100']
environment:
DATABASE_HOST: db
DATABASE_PASSWORD: change-me
volumes: [curator-data:/data]
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: change-me

Build on it

  • Call the API

    Every route the web app uses, with what a TV’s failures come back as: asleep, silent, refused or rejected.

  • Use the Frame client

    The art-mode protocol as a TypeScript package, with a probe to run against a real TV and a fake TV for the tests.

  • Work on curator

    A pnpm and turbo workspace: the API on ServerKit Fastify, the web app on React and Mantine, and Postgres in Docker.