# ReelPile — the manual > A macOS app that keeps video references on a board that plays. Paste a link and the clip lands on the canvas, stays where you put it, and plays from your own disk. Source: https://reelpile.com/manual — this file is the same text, in one piece. ReelPile runs on macOS on Apple Silicon and costs $30 once. --- # ReelPile — what it actually does Each feature is written as: **what it does** → **how you reach it** → **what it does not do**. The last part is not modesty. It is there so nothing in this manual promises you something the app cannot deliver. --- ## Contents 1. [What ReelPile is](#1) 2. [Getting material in](#2) 3. [The card](#3) 4. [The canvas](#4) 5. [Playback](#5) 6. [Boards and nested folders](#6) 7. [Search](#7) 8. [Music recognition](#8) 9. [The full-screen player](#9) 10. [Text notes](#10) 11. [Getting a file back out](#11) 12. [Sending a board to a client](#12) 13. [Where your data lives](#13) 14. [Instagram sign-in](#14) 15. [Settings](#15) 16. [Updates](#16) 17. [Platform, price, licences](#17) 18. [What ReelPile does not do](#18) --- ## 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 and no trial.** A licence key, entered once on first launch, is the only thing between installing ReelPile and using it. It is verified on your own machine and never contacts a server, so it works offline and cannot be switched off from outside. ### 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 short clips — *Harbour at dusk*, *Stairwell, top down*, *Pour, macro* and *Ridge line, wide*. They ship inside the installer as files, not as links, so the board **plays offline the first time it is opened**, before anything has been pasted and before the machine has been asked for a network. All four were rendered from scratch for this purpose and belong to ReelPile outright: there is no other rights holder and no licence to observe. That is deliberate. The demo board used to be four YouTube links fetched on first boot, which was the one place the app reached for YouTube before the user had pasted anything at all — and stock footage was considered and rejected, because those licences permit reuse but are silent on bundling a static copy inside a paid installer. Footage rendered here has nothing to be silent about. Each of the four carries an invented author handle and invented view, like and comment counts, so a fresh board looks like a working one rather than four bare rectangles. That is honest only because the footage underneath is ours: no real creator is misattributed and no real engagement is faked on anybody's behalf. --- ## 2. 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). 4. **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. 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. **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. 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. ### 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. --- ## 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 — or `Downloading…` while the background queue is fetching this particular card. A `⚠` badge with a plain-language reason when something went wrong: the post was removed or made private, it needs a sign-in, the platform is asking us to slow down, or the fetch simply failed. Four different reasons, one badge. - **`Converting…`** instead of a poster while a file you brought in is being re-encoded into something the player can open (section 2). - **An audio card looks different on purpose.** No poster, no video, no hover: a `♪` mark, the name and the duration. There is no frame to show, so it does not pretend to have one. - **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 | Button | Tooltip | What it does | |---|---|---| | `⬇` | Download video | saves the video file | | `♪` | Download music | pulls the audio out as an mp3 | | `⬇` | Download image | saves the image (photo posts only) | | `⧉` | Copy link | copies the original URL | | `⟳` | Re-sync | fetches the clip again, or repairs it | | `⇄` | Move to board | moves it to another board | | `×` | Delete | deletes the card | **The first three appear only when there is something to hand you, and the rule is stricter than it sounds.** A **YouTube card never shows either download button** — not "when there is nothing to save", but always, because ReelPile never has that file and never will. A **silent clip shows no ♪ Download music**, even though its file is right there. An **audio card** shows `♪` but no `⬇ Download video`, because there is no video to give. So a card carries anywhere from one button to three. **`⟳ Re-sync` does one of two things, depending on what is wrong.** On a card that came from a link it fetches the clip again from where it came — except a YouTube card, where there was never anything to fetch, so it simply re-checks whether the link still embeds. On a file you brought in yourself there is no remote to fetch from, so it repairs instead: a conversion that failed is attempted once more, on the untouched original. Either way the button reports what it actually did rather than claiming a success it did not have. It also refuses to spin for nothing. A working silent clip counts, technically, as a file that "needs converting" — remuxing a silent file leaves it silent — so a broad rule would have re-run ffmpeg on it at every press, forever. `⟳` acts only when the card is genuinely complaining. --- ## 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. **Sound on the board, and only where it makes sense.** In **Hover** mode — the default — the card under your cursor plays **with sound**. Move off it and it goes quiet again. Every other playing card stays muted, and the numeric modes (4, 8, 16, All) are silent throughout: a dozen clips playing at once with their audio on is noise, not a board. **What it does not do:** a card on the board is a looping video **with no controls**. You cannot scrub, pause or seek on the card — that is what the full-screen player is for. --- ## 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. --- ## 7. Search 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 that finds something.** A card that already has a track is not re-identified on a re-download. A card that never resolved one — no music in the clip, or a lookup that failed — is asked again on every re-download, because "no track" and "never asked" look identical from the outside. - **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**, never at runtime — and so does everything else in `bin/`. Nothing here reaches out and replaces itself. yt-dlp used to: it ran its own `-U` at start-up and once a day, and that was removed deliberately, because the yt-dlp ReelPile ships is built **without** the YouTube extractor and an upstream self-update would have quietly put it back within a day. --- ## 9. The full-screen player Clicking a card opens it. - A locally downloaded video plays immediately **with full controls**, autostarted and looping. - A YouTube card always plays through YouTube's official player, and a Vimeo card does too when there is no local file. - 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. - **Being converted** → *"Converting this file so it can play here. It stays on the board — this takes a minute for a big clip."* - **An audio card** opens to plain audio controls, started, with no picture. - Actions: **⇄ Move**, **⟳ Re-sync**, **⬇ Video**, **♪ Music**, **⬇ Image**, **⧉ Copy**, **× Delete** — the three download buttons on the same terms as on the card (section 3), so a YouTube card shows none of them and a silent clip shows no **♪ Music**. - **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 Up to three buttons on the card, repeated in the player. Which of them you get depends on what the card actually holds — see section 3: a YouTube card offers none of them, a silent clip offers no music, an audio card offers no video. **⬇ 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:///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. The link lives on `share.reelpile.com`. The long tail after `/b/` is not an id that could be shorter and prettier — it is the secret itself, 128 random bits, and it is the only thing between a private board and anybody who guesses. That is why the link looks the way it does. ### 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/.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 . 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 the write key are stored in `db.json`, so they travel in **every** backup — the plain `⬇ Backup` download and each silent auto-snapshot, not only the full one. Treat any backup of a library with a published board as carrying a live credential: the write key is what lets its holder overwrite or take down a link a client is looking at. 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: `.mp4`, poster `.jpg`, carousel `.1.jpg`, extracted audio `.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/`, in two shapes: `db-2026-07-24T10-32-27.json` for the every-five-minutes kind and `db-start-2026-07-24T10-32-27.json` for the one taken at launch. - A snapshot is a copy of **`db.json`**: boards, cards, positions, notes. **Neither videos nor the Instagram cookie are in it** — but if you have ever published a board, it does carry that board's **write key**, the credential that can overwrite or unpublish the link your client is looking at. The same is true of the `⬇ Backup` download, which serves `db.json` whole. It is not a secret-free file. - 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 | Button | What it does | |---|---| | `⬇ Backup` | downloads `db.json`: boards and cards, no video | | `↩ Restore` | loads 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. **There is no update button here for any of them**, and the note says why in four words: *"Tools update with the app."* There used to be one, for yt-dlp; it was removed on purpose (section 8). **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: | Tool | Licence | |---|---| | ffmpeg | **GPL-2.0-or-later** | | gallery-dl | **GPLv2** | | yt-dlp | Unlicense (public domain) | | shazamio | MIT | **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 **support@reelpile.com** 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 on the board. Sound there is limited on purpose: the hovered card plays with it, every other playing card is muted. 6. **720p is what you get, almost always.** The download format asks for it first and falls back through looser options, the last of which have no height limit — so a clip offered only above 720p arrives large rather than not at all. 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 or trial.** A licence key, entered once, is the only thing asked of you. 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. **There is no About box and no credits screen.** The GPL notices themselves are surfaced: Settings → Licences → **Third-party notices** opens `NOTICES.txt`, which ships inside the app and carries the full third-party licence list and the written offer of source required by GPL §3(b).