the manual

How reelpile works.

Every feature, what it does, how you reach it — and what it does not do. Written against the app that ships, not against a plan for it.

1. What reelpile is

A native macOS desktop app. Three layers inside: a Tauri (Rust) shell, a local Node server bound to 127.0.0.1 only (port 5181 by default; if it is taken the app picks a free one and remembers it), and a React interface rendered in that window as a page on localhost.

The idea: you paste a link to a clip, the app downloads the file to your own disk, pulls the metadata, identifies the music, and drops a card onto an endless canvas. Cards play on the board itself. Everything stays with you.

There is no account, no sign-up, no subscription, no licence key and no trial. The app is unconditionally fully functional from first launch. Payment has to be handled outside the product; nothing inside it gates anything.

First launch

A welcome screen, Welcome to ReelPile, reading "Start a new board, or bring across a library you already have", with Start fresh and Import library (the second copies boards and video files from another install; nothing is moved or deleted).

The app also seeds one board called Examples with four Blender Foundation open movies: Big Buck Bunny, Sintel, Tears of Steel, Elephants Dream. They ship as links only and download on the user's own machine. All four are Creative Commons Attribution, so even the seeded links are licence-clean.

2. Getting material in

Four ways in, all feeding one pipeline:

  • The toolbar field, Paste a YouTube, TikTok, Instagram or Vimeo link…, then Enter or + Add. Several links separated by whitespace are accepted at once.
  • The File button (also Upload file in the menu) picks video or image files from disk, multiple at a time.
  • Drag a file or a link straight onto the board.
  • ⌘V anywhere on the board pastes an image from the clipboard, or a link. (Not while your cursor is in a text field.)

What happens after you press Add

In this exact order:

  1. The platform is detected from the hostname.
  2. For YouTube and Vimeo an embed URL is built, as a fallback if the download fails.
  3. yt-dlp fetches the metadata: title, author/channel, thumbnail, views, likes, comments, duration, upload date, and the description (kept to 2000 characters).
  4. yt-dlp downloads the video. Note: the format is capped at 720p and below by design. This keeps libraries manageable, but it means reelpile is not a way to pull a 4K master.
  5. ffmpeg checks that the file really has a video track. If only audio arrived (the classic Instagram-without-a-login failure) the file is deleted and the card records an honest error.
  6. ffmpeg normalises: anything that is not already H.264 + AAC is re-encoded (libx264 -preset veryfast -crf 23, audio always AAC 160k, +faststart). The reason is concrete: the embedded WebKit view silently drops audio from VP9-in-MP4.
  7. If the video path fails, gallery-dl is tried instead; it handles photo posts and carousels, which yt-dlp cannot.
  8. ffmpeg makes the poster. One frame at 0.5 s.
  9. The music is identified (section 8).
  10. For photo posts, gallery-dl -j runs once more to recover author and caption.

This takes time, and there is no progress bar. The request is synchronous: it returns only once the download, transcode, poster and Shazam lookup have all finished. On a long or slow clip that is seconds, sometimes minutes. A card does not appear instantly, and nothing here pretends otherwise.

Which platforms actually work

Five are recognised by name: YouTube, Vimeo, TikTok, Instagram, and a direct file URL (.mp4/.webm/.mov/.m4v). Everything else is labelled Link.

But the platform label is cosmetic. The download path does not branch on it: every URL goes into the same yt-dlp → gallery-dl chain. So anything yt-dlp supports will import, it just shows up as a generic link. And a link from a "supported" domain that yt-dlp cannot handle fails like any other.

So the honest way to read it is: those five by name, and most other video links.

Per-platform reality

  • YouTube / Vimeo. The only two with an embed. So a failed download is not even recorded as an error: the card quietly plays through the platform's iframe instead.
  • Instagram. What the app is really built around. Anonymous access is rate- limited and serves video split into DASH pieces, hence the "is there actually a video track?" guard. With a sign-in it is reliable. Photo posts and carousels go through gallery-dl only.
  • TikTok. No special handling; plain yt-dlp.

When something fails

Errors are classified into four kinds: rate-limit (asked to slow down, resolves itself), login (a sign-in is needed), unavailable (deleted or private; these are abandoned, never retried), and failed. The card is still created, with a badge.

The background queue

A single-slot background queue quietly re-downloads whatever did not arrive. It:

  • runs only while you are not working, paused during your own jobs and for five seconds after your last gesture;
  • waits a random 30–90 seconds between attempts (a steady rhythm is itself a robot signature);
  • gives each card three attempts, except rate-limits, which do not count;
  • stops completely on a login error until a fresh cookie file appears;
  • backs off 15 minutes, then 60 on a rate-limit.

Separately, Update old cards in the menu is a manual sweep over older cards with no caption, filling in caption, author and title.

3. The card

Top to bottom:

  • Video or image. A poster until the video is ready. Video not downloaded if the file is not on this machine. A badge with a plain-language reason if the post is gone.
  • Platform tag in the corner.
  • Frame counter (1/8) for carousels.
  • A row of buttons on hover.
  • The title.
  • ♪ Artist — Track, clickable, opens the track in your browser.
  • @author, also a button: clicking it puts the author into the search box.
  • Stats: ▶ views, ♥ likes, 💬 comments, ⏱ duration, abbreviated (12.3K, 1.4M). Only what the source actually reported. Your own uploads and some posts arrive with none.
  • A resize handle in the bottom-right corner.

The seven buttons

ButtonTooltipWhat it does
Download videosaves the video file
Download musicpulls the audio out as an mp3
Download imagesaves the image (photo posts only)
Copy linkcopies the original URL
Re-syncre-downloads the clip
Move to boardmoves it to another board
×Deletedeletes the card

The first three appear only when there is something to save.

4. The canvas

Dragging. Grab a card, put it anywhere. If you drop it more than halfway onto another card, the app spirals it out to the nearest clear spot by itself, so cards never end up stacked.

Resizing. The bottom-right corner. Minimum 60 px. A new card arrives 300 px wide, so the board keeps one consistent scale.

Panning. Drag empty canvas. Pinch works on a trackpad.

Zoom. A small bar: +, the percentage, , fit, reset. Range 5 %–250 %, steps of ×1.2. fit frames the whole board in the window; reset goes to exactly 100 %.

Every board remembers its own view. Pan and zoom are stored per board, so switching boards returns you where you were.

Selecting several. Drag a rubber band across empty canvas, or Shift-click. A bar appears: N selected, Move to ▾, Clear. Drag one and the whole set moves. Esc clears, Delete/Backspace deletes.

Arrange

▦ Arrange re-lays the whole board.

What it actually does: cards are packed left to right, wrapping to a new row once the row would exceed 1600 units, with a 24-unit gap, and each row's height is set by its own tallest card. It never resizes anything: a manual resize is a deliberate act by the user, and the code respects that.

The consequence: these are rows, not a grid. The right edge comes out ragged and rows differ in height.

Undo (⌘Z), and what it does not cover

⌘Z only undoes text notes and drawings. The undo stack holds shapes, not card positions. Moving a card is not undoable; you move it back by hand. The stack holds 60 steps.

5. Playback

A Playing videos dropdown in the toolbar (repeated in the menu): Off · Hover · 4 · 8 · 16 · All.

The default is Hover. Out of the box nothing autoplays: the board is a wall of posters and only the card under your cursor plays. There is a short delay before it starts, so cards do not flare up while you sweep the mouse across. Hover does not exist on touch screens, so that mode does nothing there.

All means 16, not all. Literally: the setting resolves to 16. Playing dozens of videos at once saturates the machine's video decoders and freezes most of them. So the 16 cards nearest the centre of the view play, and the rest show a poster. The numeric options (4/8/16) are exact.

Two more conditions:

  • a card only plays if it is at least 40 px wide on screen, so zoomed far out slivers do not start;
  • the playing set is recomputed about 150 ms after you stop panning or zooming, so nothing thrashes mid-gesture.

Opening the full-screen player pauses the whole board and resumes it on close, so the player and the board do not fight over decoders.

What it does not do: a card on the board is a muted, looping video with no controls. You cannot scrub, pause or seek on the card. There is no sound on the board at all. Sound only exists in the full-screen player.

6. Boards and nested folders

A rail on the left.

  • Create: the New board field at the bottom, then Enter or +. The new board is selected immediately, and the canvas goes empty for a moment, which is expected.
  • Rename: double-click the name.
  • Emoji and colour: click the board's icon for an emoji field (up to 4 characters) and six colour swatches.
  • Reorder: drag rows up and down.
  • Nest: drop one board onto another and it moves inside. The parent expands automatically. Cycles are prevented; depth is unlimited.
  • Un-nest: while dragging a nested board, an ↑ Move to top level zone appears.
  • Expand/collapse: the / caret. Remembered.
  • A count next to each name.
  • Delete: ×, confirming "Delete this board and all its references?". If the board is published and the link cannot be taken down, the delete is refused rather than leaving an orphaned live link.
  • The rail collapses with « and can be dragged wider. Both are remembered.

The app reopens on the board you had open last.

Two identical fields, one in the rail and one in the toolbar, both Search title, author, track…. Filtering is instant, as you type.

It searches exactly four fields: the clip's title, the author, the track title and the artist name. Substring, case-insensitive.

It does not search: captions or descriptions, URLs, board names, or the text of your notes.

Search works within the current board, not across the whole library. Results sort newest first.

Clicking @author on a card puts that author into the search, a fast way to gather everything by one person.

⚠️ Gotcha: publishing a board clears the search box, because every card has to be measured before it is sent and filtered cards are not rendered. The filter does not come back.

8. Music recognition

This is real Shazam. Not a lookalike, not a guess from the title.

After the download, ffmpeg cuts the first 20 seconds, mixes it to mono 16 kHz WAV, and hands that to a bundled tool called recognise: a PyInstaller-frozen build of the shazamio library, which queries Shazam's own service. It returns the track title, the artist and a link to the track. All three land on the card, and the title and artist are searchable.

Worth knowing:

  • It needs the internet. Offline, there is no result.
  • No API key is involved. This is an unofficial client, not a Shazam SDK; if those endpoints ever close, the feature stops, and only an app update can fix it.
  • It runs once per card. A card that already has a track is not re-identified on a re-download.
  • Images and carousels never get music.
  • Failure is silent. A clip with no music, a network failure and a broken recogniser build all look identical: the line simply is not there. No error is shown. That is deliberate, but it means a broken recogniser goes unnoticed.
  • It updates with the app, not at runtime (unlike yt-dlp).

9. The full-screen player

Clicking a card opens it.

  • A locally downloaded video plays immediately with full controls, autostarted and looping.
  • YouTube and Vimeo without a local file play through the official iframe.
  • Image carousels page with ‹ ›, with ←/→, and a 3 / 8 counter.
  • No file on this machine → "Video not downloaded on this machine" and a Download now button that jumps this card to the front of the background queue.
  • Nothing playable → a plain-language reason and Open original ↗, which opens the post in your system browser.
  • Metadata below: title, @author (linked to the original), the track line, and the full post caption. The caption exists only here, never on the card.
  • Actions: ⇄ Move, ⟳ Re-sync, ⬇ Video, ♪ Music, ⬇ Image, ⧉ Copy, × Delete.
  • Esc closes; so does clicking the backdrop.

10. Text notes

A tool dock at the bottom left holds exactly two tools: Select / move and T (Text). Plus Undo (⌘Z) and Clear all drawings (which asks first).

Pick T, click the canvas, type, press Enter. With a note selected, a contextual bar appears: font, size (/+) and colour (a palette plus a custom picker). Notes can be moved and can be selected together with cards (rubber band or Shift-click) and then the whole mixed group drags as one.

On drawing. Four more tools exist in the data model (pen, arrow, rectangle, eraser) but they are removed from the palette and you cannot draw with them. Existing drawings still render and are not damaged; only authoring was removed.

⚠️ Also: publishing a board drops drawings entirely, while text notes do travel. See section 12.

11. Getting a file back out

Three buttons on the card, repeated in the player.

⬇ Download video. Hands over the exact file on disk, unmodified. No transcode at save time. The filename is the clip title, stripped to letters, digits, Cyrillic, spaces and hyphens, and capped at 60 characters.

⚠️ It is always named .mp4, even in the rare case where the container is really .mov or .mkv (possible when a clip arrived via gallery-dl).

♪ Download music. The first time, ffmpeg extracts the audio to 192 kbps MP3; after that it is served from cache. ⚠️ That mp3 stays in the media folder forever and is never cleaned up; it inflates the library and rides along in every full backup.

⧉ Copy link. Puts the original URL on the clipboard.

⬇ Download image. Photo posts, saves the first image.

Where the file lands

Every one of these buttons is an ordinary link pointing at reelpile's own local server. The app never puts up a save panel of its own, so the file is written wherever your Mac already saves downloads.

What is not there

There is no drag into a video editor. Not Premiere, not Resolve, not Finder. No drag-out of any kind is implemented. A file leaves the app only through the download button.

There is no PNG export of the board. It existed and was removed.

12. Sending a board to a client

The share button in the toolbar, or Share… in the menu.

What happens

The app builds one self-contained HTML page from the board and uploads it, with the cover images, to a Cloudflare Worker. The link looks like:

https://<reelpile's share host>/b/<22 random characters>/

Twenty-two random characters is the whole of the secrecy: the address is not guessable, and it is not listed anywhere. Anyone you give it to can open it.

Exactly what travels, and what does not

Uploaded: the board's name and emoji; per card, an id, position and size, the poster image (JPEG), author, caption, views, likes, duration, {track, artist}, the embed URL, a link to the original, a platform label, and two flags (unavailable / no link). Plus text notes (text, position, size, colour).

Never uploaded: video files, ever; audio; the full original URL (only a stripped version); any path on your disk; the filename of your own uploads; comments; dates; board ids; error text; drawings.

The module states the rule itself: "A shared board carries POSTERS ONLY — no video path, no local file path, nothing that reveals this machine."

This is verified three ways: the uploader only ever writes p/<id>.jpg and index.html; the Worker rejects any other path shape with a 400; and playback on the client's side runs through the platform's own iframe, not your server.

What the client sees

They open the link in any browser. On a desktop it is the board with your layout preserved, pannable and zoomable; on a phone it collapses into a single column (ordered at publish time by rows top to bottom, left to right within a row, so a note above a group reads as its heading).

Clicking a card opens the platform's player, a link out to the original, a heart, and close. No account, nothing to sign up for.

Hearts and review: two limits worth knowing

Your review link is the same link with ?liked=1 on the end. Open it and picked cards are outlined in red, the rest dim, and the header reads "Client marked 7 of 24".

Limit one: one link, one client. The picks live in a single record that is overwritten wholesale: last write wins. Send one link to two people and the second erases the first's choices. Publish separately for each client.

Limit two: ?liked=1 is not a secret. Anyone holding the client link can append it and see the picks, and technically overwrite them. The only protection is that the link itself is unguessable.

Also: the picks never come back into the app. You read them in a browser. There is no "show only the picked ones" filter and no export.

Managing a published board

  • Update re-uploads the current state. The link does not change.
  • Unpublish asks "Unpublish this board? The link you sent will stop working." It removes everything, including the client's picks. Publishing again afterwards mints a new link; the old one is dead for good.
  • No expiry. The link lives until you take it down.
  • One board, one publication. Limit: 20 new publications per IP per hour.
  • The page is marked noindex,nofollow, though the host serves no robots.txt.
  • The panel shows "Last published <date>. The client sees the board as it was then."

Things to say plainly rather than gloss over

  • No password. The link is the only key. Anyone it is forwarded to can open it.
  • Your own uploaded mp4s are dead weight on a shared board. They have no platform link, so the client sees a still frame they can never play, badged no link to open. This will be the most common support question.
  • Playback goes through Instagram/YouTube, so the platform can see your client opening it.
  • Posters are cached on the client's side for a year. After an Update, someone who already opened the board may keep seeing the old thumbnail for a long time.
  • Publishing is not instant: each Instagram post is probed to see whether it is still alive (6 in parallel, 5 s timeout).
  • Publications live only on this machine. The share id and write key are stored locally and travel in the full backup. On a fresh machine without restoring that backup, you cannot update or unpublish your own live link.

13. Where your data lives

~/Library/Application Support/app.reelpile.desktop/
    data/    db.json, ig-cookies.txt, backups/
    media/   videos, posters, images, extracted mp3s
    bin/     yt-dlp, ffmpeg, gallery-dl, recognise
    logs/    server.log, shell.log

The path is shown in Settings under Data. ⚠️ It is plain text: there is no "reveal in Finder" button and no way to change the folder from inside the app. The only Finder-opening action is Open logs.

The format is plain JSON. A single db.json. No SQLite. Writes are atomic (temp file, then rename), so a crash mid-save cannot corrupt the library.

Media files are named by the card's UUID: <uuid>.mp4, poster <uuid>.jpg, carousel <uuid>.1.jpg, extracted audio <uuid>.mp3.

Auto-backup, as it really is

  • A snapshot is taken on save, at most once every 5 minutes, plus one at every app launch.
  • The last 60 are kept. (The cap is shared between the two kinds, so in practice fewer than 60 of either.)
  • They live in data/backups/, named db-2026-07-24T10-32-27.json.
  • A snapshot contains only db.json: boards, cards, positions, notes. Neither videos nor the Instagram cookie are in it.
  • In Settings this is one row, Auto-backup, reading on · N snapshots kept. There is no off switch. "on" is hard-coded.

How you actually use a snapshot matters. Automatically, they save you in exactly one situation: if db.json is missing or unreadable at start-up, the newest snapshot is loaded silently. There is no interface for picking a snapshot by hand. The ↩ Restore button only accepts a file you exported yourself with ⬇ Backup. To use an auto-snapshot you have to open the folder in Finder, and no button in the app opens that folder.

The four backup buttons

ButtonWhat it does
⬇ Backupdownloads db.json: boards and cards, no video
↩ Restoreloads such a JSON back
⬇ Full backup (with videos & login)a .tar of the library: database + all media + the Instagram cookie
↩ Restore full backup…native file picker, unpacks it back

The note underneath, verbatim: "A full backup carries your videos and your Instagram login — keep it private. Restoring replaces your current board. Use ReelPile on one machine at a time."

Restoring is destructive and asks first; a copy of the current state is genuinely saved before it is replaced.

14. Instagram sign-in

Settings → Account → Log in to Instagram.

Instagram's own login page opens in a separate 520×720 window. You sign in there. The app checks that window's cookies once a second for up to five minutes and, the moment a session appears, writes them to data/ig-cookies.txt: atomically, mode 0600, then closes the window.

The code's own comment: "we never see, store, or transmit the password", and that is accurate: it reads cookies from a finished window and never touches the password field.

The interface says: "Opens Instagram's login page in a window — sign in there (a secondary account is safest). No file needed." The secondary-account advice is sound; keep it.

  • Once connected the row reads Instagram: connected ✓ — log in again.
  • If the session goes stale, a banner appears: "Instagram needs you to sign in again." with a Reconnect Instagram button. It clears itself as soon as the cookie file is refreshed; no restart needed.
  • Disconnect Instagram deletes the file.
  • The cookie is included in a full backup, hence the "keep it private" warning.

Cookies are not imported from your system browser. This window is the only path.

15. Settings

Opened only by the button in the toolbar. No keyboard shortcut, no menu-bar item. Closed with × or by clicking the backdrop.

Understand the shape of it: there are no ordinary preferences in Settings. It is status readouts and action buttons. Theme and playback live in the toolbar instead.

Engines. Four status rows: ytdlp, ffmpeg, gallerydl, recognise (version / ok / missing), showing Checking… for the first few seconds. A Check for updates button updates yt-dlp only; the note says so: "Only yt-dlp updates here; gallery-dl and the music recogniser update with the app."

Stability. Restart engine and Open logs.

App. The app version; a check for updates button; a Videos row with the background queue's status (Downloading videos — N left / All videos downloaded / Paused — log in to Instagram… / Paused — Instagram asked us to slow down; resuming in N min); Port; Data (the path).

Library. Auto-backup and the four backup buttons (section 13).

Account. Instagram (section 14).

Sync. Cloud sync does not exist in shipped builds. Every /api/sync/* endpoint answers 404 sync is disabled, and there is nothing to reach in the interface.

16. Updates

Two independent layers.

Layer 1: the hot update (interface and local server)

Replaces the built interface and the Node server. Not the shell, not the bundled tools.

  • Checks 20 seconds after start, then every 5 minutes.
  • Applies completely silently, and only when you are not mid-gesture; the page simply reloads. No banner, no version number, no consent.
  • Packages are signature-verified against a public key baked into the app; without that key the channel is inert.
  • Cannot be disabled, and there is no manual check button.
  • If an update fails to start you get "Update N didn't start — reverted to the previous version.", and Settings offers Revert to the bundled version (a one-way door: that version is blocklisted for good).

Layer 2: the native updater (the whole app)

Replaces the entire signed .app.

  • Checks on three triggers: at launch; when the window regains focus (at most once every 15 minutes); and on a timer every 6 hours. The third exists because on macOS closing a window does not end the process, so a user could sit on an old version for a week without ever seeing a banner.
  • Manual check: Settings → App.
  • A banner appears bottom-right, currently all in Russian: version available, Update / Later, then a download percentage, then a restart notice.
  • The restart is automatic once the download completes; you are not asked twice.
  • Later is not remembered: the next check offers the same version again.
  • Errors are completely silent. A failed check or download shows nothing at all.
  • Release notes are downloaded and then never displayed.
  • Cannot be disabled.

17. Platform, price, licences

macOS 10.15 (Catalina) and later, on Apple Silicon. That is what the update feed publishes, and it is the only build there is. There is no Windows and no Linux version.

Signed and notarised, and enforced rather than promised: the release script runs a Gatekeeper assessment, checks that a notarisation ticket is stapled to the bundle, and refuses to publish if either fails. The bundled tools are separately signed with a hardened runtime.

Price: $30 once, and every update after. Updates are free and never gated.

Licences

Bundled inside the app:

ToolLicence
ffmpegGPL-2.0-or-later
gallery-dlGPLv2
yt-dlpUnlicense (public domain)
shazamioMIT

ffmpeg and gallery-dl are GPL. If you have a copy of reelpile you are entitled to the corresponding source of those two components. Write to [email protected] and it will be sent to you. The exact versions and their upstream addresses are recorded in the build's binaries.lock.json.

18. What reelpile does not do

An honest list, so nothing here is a surprise after you buy.

  1. No drag into an editor. Not Premiere, not Resolve, not Finder. Files leave only through the download button.
  2. No PNG export of the board. Removed.
  3. No drawing. Pen, arrows and rectangles are hidden from the palette. Text notes only.
  4. The whole board does not play. The ceiling is 16 cards, and by default nothing plays except the card under the cursor.
  5. No scrubbing on a card. No controls and no sound on the board.
  6. Nothing above 720p. The cap is built into the download format.
  7. Moving a card is not undoable. ⌘Z covers notes and drawings only.
  8. Arrange makes rows, not a grid. Ragged right edge; sizes are never changed.
  9. Auto-snapshots are not browsable. 60 exist; no interface reaches them.
  10. The data folder cannot be changed or revealed from the app.
  11. No sync between machines. The code exists but is switched off in every build.
  12. No account, sign-up, licence key or trial.
  13. Not fully offline. Importing needs the network (yt-dlp) and music recognition needs the network (Shazam). Offline, you can browse what you already have.
  14. No password on a published board. Only an unguessable link.
  15. One shared link cannot serve two clients. The second overwrites the first's picks.
  16. Client picks do not come back into the app. You read them in a browser.
  17. Update failures are never shown.
  18. Search does not cover captions, URLs or notes. Only title, author, track and artist.
  19. Windows is not a shipping product (unsigned, no updates).
  20. GPL notices are not surfaced in the app.

Something here wrong, or something missing? Tell us — the manual is corrected against the app, not the other way round.