instaGallery

A private, self-hosted archive for organizing Instagram saved posts

Type
Self-hosted web app
Role
Solo project
Runs on
My Mac, reached over Tailscale
Scale
1,000+ posts, ~30 categories
Timeline
3 months, 53 commits
Built with
Python, FastAPI, SQLite, instaloader, pytest, JavaScript, Tailwind, DaisyUI, Tailscale

I'd spend hours on Instagram, scrolling and saving posts that I wanted to come back to later: recipes, craft ideas, DIY projects, etc... Unfortunately the "saved" tab on the Instagram app leads to one long list of disorganized posts, so I might as well have been tossing the saved posts into the void.

I wanted a way to organize them into categories, and to be able to search through them quickly. So I built instaGallery, a private, self-hosted web app that copies my saved posts onto my own computer and gives me a fast, searchable gallery for sorting them.

It runs entirely on my Mac with no cloud service. I set up a private network with Tailscale, so I can also access it from my iPad for easy use in the kitchen.

Post images and captions are blurred to respect the original creators. Click a video to pause it.

The gallery at a glance

Over 1,000 saved Instagram posts, finally organized. I created a "pinned" bar at the top to save posts I'm currently working on, like recipes I'm considering making soon.

Getting the actual content

Instagram's official data export only includes links, not the posts themselves. I built an ingest pipeline on instaloader that downloads the real media and captions.

Instagram's bot detection blocks normal scripted logins, so the pipeline signs in with the session from my regular browser instead. New saves are merged into a SQLite database, and a one-click "Refresh" in the app runs the whole pipeline in the background.

Organizing

Posts go into two-level categories, like food → pasta. Categories are assigned from a picker rather than typed, so a typo can't quietly create a near-duplicate category.

"Uncategorized" views (for the whole archive or within a single category) show me which posts still need sorting.

Browsing by category

Categories and sub-categories narrow the grid instantly; every active filter shows as a removable chip.

Jumping to a category from any post

Every post's tag is a shortcut: click it to see everything else filed there.

Organizing a post

Sorting the stragglers: assign a category and sub-category from the detail view.

Bulk tagging

Tag several posts at once with selection mode. This was especially useful for the first pass through my 1,000+ saved posts.

Notes

Each post has a notes section that opens with a recipe template, since the vast majority of my saved posts are recipe-related. Notes are saved to the database and also written to plain Markdown files, so they're still readable without the app.

Recipe notes

Frequently post captions won't contain the recipe, but sometimes it's easy enough to get it from watching the video. The Note feature lets me transcribe it once, saved both in the app and as a plain Markdown file.

Everyday features

Managing categories

Editing tools stay hidden until you need them. “Edit categories” reveals create, rename and delete.

Light and dark themes

Light and dark themes; the category colors adapt to each.

Challenges

"Load more" showed duplicate posts

The grid shuffles posts in a random but repeatable order on every change in filter. I did this to make sure I was surfacing as many new posts as possible, instead of the same date-time order.

Early on, categorizing a post while scrolling made the "Load more" button bring back posts I'd already scrolled past earlier on the same filter. This was a consequence of my "reshuffle post order at any opportunity." Any change in the number of matching posts (such as changing the category of one post) reshuffled the whole list.

I switched to giving each post a fixed position from a hash of the shuffle seed and its ID, and to loading the next page after the last post shown rather than by page number. Now posts can come and go mid-scroll without anything repeating or being skipped.

Making it work on the iPad

On the iPad, scrolling inside an open post also scrolled the grid behind it, and Safari kept showing old versions of the app after I'd changed it. I fixed the first by locking the page in place while a post is open, and the second by telling the browser to always check for updated files.

What's next

Next I'd like to sync new saves automatically on a schedule instead of with a button, and improve the layout on phones; it's built mainly for my Mac and iPad.

How it's built

The filter logic, the subtlest part of the app, is covered by an automated test suite. The front end is split into small JavaScript modules that all share one source of truth for the current filter, so the sidebar, filter chips and grid can't get out of sync.

The front end is plain JavaScript with Tailwind and DaisyUI, with no build step.