This branch is the only place the fetching tooling still exists, and it names the fetch host's public IP, the cookie's browser profile, the NAS path and the accounts being archived. main's history was rewritten, the GitHub repo deleted and recreated, and 22 ghcr images pruned to get that material out of public view — one push of this branch undoes all of it. Deliberately at the top of the README rather than in a separate file: gitea renders README.md on the branch page, which is where someone actually looks before pushing. That does diverge from main, but only by an addition, which a merge cannot silently drop — unlike the CLAUDE.md/package.json case, where main was DELETING lines this branch kept and every merge re-applied the deletion. Worst case here is a trivial conflict if main edits the same region, and the block says to keep both. Also records that the pre-push hook is local and unversioned, since a fresh clone is the realistic way this goes wrong. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UXfdJu7QhSJLr47K7koTDF
136 lines
6.0 KiB
Markdown
136 lines
6.0 KiB
Markdown
# InstaArchive Viewer
|
|
|
|
> [!CAUTION]
|
|
> ## This branch must not be published to GitHub
|
|
>
|
|
> You are on **`tooling`**. It is the only branch that still contains the
|
|
> archive-fetching scripts and their docs, and those name things `main` was
|
|
> rewritten to remove:
|
|
>
|
|
> - the fetch host's **public IP** (`docs/gallery-dl.md`)
|
|
> - the browser profile the session cookie is read from
|
|
> - the NAS archive path
|
|
> - the **list of Instagram accounts being archived**
|
|
>
|
|
> On 2026-08-20 `main`'s entire history was rewritten with `git filter-repo`,
|
|
> the GitHub repo was deleted and recreated, and 22 container images were
|
|
> pruned from ghcr — all to remove exactly this material from public view.
|
|
> **One push of this branch to GitHub undoes all of it**, and the second
|
|
> cleanup would be harder than the first: force-push residue stayed reachable
|
|
> by SHA until the repo itself was deleted.
|
|
>
|
|
> Guards in place, neither of them foolproof:
|
|
>
|
|
> - `.git/hooks/pre-push` rejects any ref but `main`/tags to the GitHub remote.
|
|
> It is **local and unversioned** — a fresh clone does not have it.
|
|
> - `git config remote.github.push refs/heads/main:refs/heads/main`.
|
|
>
|
|
> Recreate both after cloning. `origin` is gitea and is the right default for
|
|
> everything; GitHub only ever needs `main` and the current release tag.
|
|
>
|
|
> *This block exists only on `tooling`. If a merge from `main` ever conflicts
|
|
> here, keep both sides — the warning is not something `main` deleted.*
|
|
|
|
|
|
A high-performance React PWA for browsing archived Instagram data with a native-feeling interface. Supports both official Instagram exports and Instaloader archives.
|
|
|
|
## Features
|
|
|
|
- **Advanced Carousel**: Seamless, zero-latency transitions between slides with intelligent preloading. Navigating between different posts is now near-instant thanks to inter-post background preloading.
|
|
- **High-Res Performance**: Handles 50MP+ images effortlessly using a background Web Worker and a memory-safe serial processing queue.
|
|
- **Persistent Local Caching**: Uses IndexedDB to store parsed archives and generated thumbnails. **Local folders** now load instantly from cache on return visits without needing to re-upload files.
|
|
- **Permalinks**: State is synchronized with the URL, allowing you to share direct links to archives, tabs, or specific posts. Navigating back to the explorer cleans up URL parameters automatically.
|
|
- **Glassy Scanning UI**: A refined, translucent white terminal experience with flicker-free, double-buffered dynamic blurred backgrounds.
|
|
- **PWA with Auto-Update**: Fully offline-capable and installable. Clients automatically receive updates when a new version is deployed to the server.
|
|
- **Local Privacy**: All processing is done client-side. Even when using the self-hosted version, your media is processed locally in your browser and never uploaded.
|
|
- **Smart Fallbacks**: Automatically detects usernames from folder names and uses the oldest archive image as a profile picture if one is missing.
|
|
- **Customizable Grid**: 1:1 or 3:4 aspect ratios with adjustable "bumps" for aesthetic alignment.
|
|
- **Story Viewer**: Native-like story experience with segmented progress bars, auto-playback, and audio controls.
|
|
- **Navigation Protection**: Intercepts accidental browser "Back" or "Refresh" actions to protect your current session.
|
|
|
|
## Deployment
|
|
|
|
### Docker (Recommended)
|
|
|
|
The easiest way to run InstaArchive is using Docker.
|
|
|
|
```bash
|
|
docker run -d \
|
|
-p 3000:3000 \
|
|
-v /path/to/your/archives:/archives:ro \
|
|
ghcr.io/ergosteur/instaarchive-viewer:latest
|
|
```
|
|
|
|
> **Note for Linux/SELinux users:** If you see "Permission Denied" in the logs, append `,z` to your volume mount: `-v /path/to/archives:/archives:ro,z`
|
|
|
|
### Docker Compose
|
|
|
|
Create a `compose.yml` file:
|
|
|
|
```yaml
|
|
services:
|
|
instaarchive:
|
|
image: ghcr.io/ergosteur/instaarchive-viewer:latest
|
|
ports:
|
|
- "3000:3000"
|
|
volumes:
|
|
- ./archives:/archives:ro,z # ,z handles SELinux permissions
|
|
```
|
|
|
|
### Troubleshooting Permissions
|
|
|
|
If the app shows "No Archives Found" and logs `EACCES: permission denied`:
|
|
|
|
1. **Check Directory Permissions**: Ensure the archive folder is world-readable:
|
|
```bash
|
|
chmod -R 755 /path/to/archives
|
|
```
|
|
2. **SELinux (Fedora/RHEL/CentOS)**: Use the `:z` flag in your volume mount as shown above.
|
|
3. **User Mapping**: The container runs as the non-root `node` user (UID 1000).
|
|
If your archives are readable only by another account, run as that user
|
|
instead — the container needs to *list* the archive directory, so `--x`
|
|
(traverse-only) permissions are not enough:
|
|
```bash
|
|
docker run --user $(stat -c '%u:%g' /path/to/archives) ...
|
|
```
|
|
In Compose:
|
|
```yaml
|
|
services:
|
|
instaarchive:
|
|
user: "1234:1234" # a UID that can read your archives
|
|
```
|
|
|
|
### Archive Index
|
|
|
|
On first start the server walks the archive root once and caches the result,
|
|
keyed by directory mtime. This matters on network storage: for a 110k-file
|
|
archive root, listing went from ~52s per request to ~0.1s. Mount a volume at
|
|
`/cache` (or set `CACHE_DIR`) so the index survives restarts, otherwise it is
|
|
rebuilt on every start.
|
|
|
|
## Supported Archive Structure
|
|
|
|
Place your archive folders inside the mounted `/archives` directory. The directory name will be used as the account username.
|
|
|
|
### Example Structure:
|
|
```text
|
|
archives/
|
|
├── wanderlust_explorer/ # Instaloader format
|
|
│ ├── 2024-01-01_12-00-00_UTC.jpg
|
|
│ ├── 2024-01-01_12-00-00_UTC.json.xz
|
|
│ └── wanderlust_explorer_profile_pic.jpg
|
|
└── pixel_architect/ # Instagram Export format
|
|
├── 2023-12-25_pixel_architect - post_123.jpg
|
|
├── 2023-12-25_pixel_architect - post_123.json
|
|
└── pixel_architect.jpg
|
|
```
|
|
|
|
## Local Development
|
|
|
|
**Prerequisites:** Node.js (LTS recommended)
|
|
|
|
1. **Install dependencies:** `npm install`
|
|
2. **Start dev server:** `npm run dev` (Frontend on port 3000)
|
|
3. **Start local backend:** `npm run server` (Optional, serves `./_sample-archives` on port 3001)
|
|
4. **Build production:** `npm run build` (Generates `./dist` for frontend and `./dist-server` for the API)
|