chapter 2 of 18

Getting material in

Four ways in, all feeding one pipeline:

  • The toolbar field, Paste a 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. A YouTube link stops here. It gets an embed URL and nothing else: no metadata call, no download, no ffmpeg. Its title, author and thumbnail come from YouTube's public oEmbed endpoint instead, and the card plays through YouTube's own player. Everything from step 3 on describes the other platforms.
  3. For Vimeo an embed URL is built, as a fallback if the download fails.
  4. yt-dlp fetches the metadata: title, author/channel, thumbnail, views, likes, comments, duration, upload date, and the description (kept to 2000 characters).
  5. yt-dlp downloads the video. The format request prefers 720p and below, and that is what nearly everything resolves to — it keeps libraries manageable and ReelPile is not built to pull a 4K master. It is a preference rather than a ceiling: the request is a cascade of eight fallbacks, and the last few carry no height limit at all, so a clip offered only above 720p arrives at whatever it is offered at rather than failing. A card with no file is a worse outcome than a large one.
  6. 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.
  7. 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.
  8. If the video path fails, gallery-dl is tried instead; it handles photo posts and carousels, which yt-dlp cannot.
  9. The poster is fetched, or failing that, made. First choice is the platform's own thumbnail, downloaded and stored locally — it is the frame the platform chose, which is usually the frame the author chose. Only if that fetch fails does ffmpeg grab one itself, and it seeks to the middle of the clip; 0.5 s is the fallback for a clip whose duration is unknown or shorter than a second. An early fixed frame too often lands in a fade from black.
  10. The music is identified (section 8).
  11. 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.

A file the player cannot open is converted in the background

The steps above describe a link. A file brought in from your own disk takes a shorter path, with one branch worth knowing about.

ReelPile plays video inside the system's own WebKit view, and that view accepts a narrow set of formats. Bring in a .mkv, an .avi, a ProRes .mov — the kind of thing that comes off a camera or out of an edit — and the card appears immediately, sits where you dropped it, and says Converting… while ffmpeg re-encodes it to H.264 and AAC in the background. Open it full-screen during that and the player says so in words: "Converting this file so it can play here. It stays on the board — this takes a minute for a big clip."

Three things about that are worth stating plainly:

  • The card is on the board the whole time. Nothing is queued out of sight and nothing has to be watched. You can carry on arranging around it.
  • Your original is not touched until the new file exists. A conversion that fails leaves the file it was given exactly as it was, which is what lets ⟳ Re-sync offer a second attempt rather than an apology.
  • The converted file takes a new name, not just new contents. This is not a detail: /media is served as static files, the browser picks its playback engine from the file extension, and it rejects video/x-matroska and video/x-msvideo whatever the bytes inside turn out to be. A conversion that kept the old name would succeed and still not play.

A sound file gets a card of its own

Drop in an audio file and it becomes a card that knows it is audio, rather than a video card with nothing to show. There is no frame to use as a poster and none to show on hover, so the card presents itself as sound and plays as sound.

The one consequence worth knowing: ⬇ Download video does not appear on an audio card, because there is no video to hand you. ♪ Download music does, and on these cards it needs no checking — a sound file is its own music.

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. Never downloaded. The bundled yt-dlp is built from source with the YouTube extractor removed, so the app is not merely declining to fetch YouTube — it has no means to. A YouTube card is a poster on the board and plays in YouTube's own player when you open it, which is why it needs a connection and will not play offline. Title, author and thumbnail come from YouTube's public oEmbed endpoint.
  • Vimeo. Has an embed too, used as a fallback: a failed download is not recorded as an error, the card quietly plays through the 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.

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