🌊 Nami
An open-source CLI media downloader for Instagram, TikTok, Facebook, and X
Nami coordinates gallery-dl and yt-dlp through a pure functional planning core and deterministic execution shell. It provides deduplicated archive indexing, multi-attempt failure classification, process isolation, and both interactive Rich terminal UI and scriptable JSON CLI modes.
Platform Matrix • Architecture • Installation • Quickstart • CLI Reference • Configuration • Diagnostics
Table of Contents
- Platform Support Matrix
- Architecture & Pipeline
- Installation
- Quickstart
- Workspace Hierarchy
- CLI Reference
- Authentication & Cookies
- Failure Taxonomy & Retry Policy
- Deterministic Exit Codes
- Configuration & Environment Variables
- Development & Verification
- License
Platform Support Matrix
| Platform | Photos & Posts | Videos & Reels | Stories | Highlights | Auth Method | Primary Engine |
|---|---|---|---|---|---|---|
| Supported | Supported | Supported | Supported | Netscape Cookie / Anonymous | gallery-dl / yt-dlp (Video Fallback) |
|
| TikTok | Limited | Supported | Unsupported | Unsupported | Browser DB / Netscape Cookie | yt-dlp / gallery-dl |
| Limited | Supported | Unsupported | Unsupported | Netscape Cookie / Anonymous | gallery-dl / yt-dlp |
|
| X (Twitter) | Limited | Supported | Unsupported | Unsupported | Netscape Cookie / Anonymous | gallery-dl / yt-dlp |
Architecture & Pipeline
Nami enforces a strict boundary between pure deterministic domain planning and stateful subprocess execution (ADR-0001):
flowchart TD
A[Target URLs / Profiles] --> B[targets.py: Canonicalization & Endpoint Expansion]
B --> C[planner.py: Pure Functional PlanStep Generator]
C --> D{NamiService Orchestrator}
D -->|Acquire File Lock| E[archive.lock]
D -->|Select Backend| F[gallery-dl Engine]
F -->|Success| G[Update archive.txt & Emit BatchResult]
F -->|FailureKind.EXTRACTOR| H[Fallback: yt-dlp Engine]
F -->|Transient Network/Timeout| I[Exponential Jittered Retry]
H -->|Success| G
H -->|Exhausted| J[Structured Failure Report]
Core Design Guarantees
- Pure Planning Layer:
targets.pyandplanner.pycompute output paths and executablePlanStepsequences without performing disk I/O or network requests. - Subprocess Isolation: Zero shell interpretation (
shell=False). Subprocesses execute via discreteargvtuples with full process-tree cleanup upon timeout or cancellation. - Atomic File Locking: Download archives (
archive.txt) are protected by non-blocking PID-keyed locks (archive.lock) to prevent concurrent process corruption. - Path Containment: All output directories are verified against
base_dirusingsafe_target_dir()to prevent path traversal or drive-escaping.
Installation
Requires Python 3.10 or newer.
python -m pip install nami
To update an existing installation:
python -m pip install --upgrade nami
Quickstart
1. Interactive Terminal UI
Run nami (or python -m nami if Python Scripts directory is not on PATH) without arguments:
nami
# or
python -m nami
╭─ Nami ───────────────────────────────────────────────────────────── v5.0.4 ─╮
│ What do you want to download? │
│ │
│ 1 Photos only │
│ 2 Videos only │
│ 3 Stories only │
│ 4 Highlights only │
│ 5 Photos + Videos │
│ 6 Stories + Highlights │
│ 7 All │
│ 8 Settings │
│ 0 Exit │
│ │
│ Save: ~/Nami/downloads │
╰─────────────────────────────────────────────────────────────────────────────╯
2. CLI Batch Invocations
# Download direct posts and reels
nami download https://www.instagram.com/p/C_EXAMPLE/ https://x.com/OpenAI/status/1234567890
# Target specific profile media
nami download https://www.instagram.com/natgeo/ --media photos,videos
# Headless batch processing with JSON stdout
nami download --profiles --platform instagram --media all --json
Workspace Hierarchy
Nami/
├── downloads/ # Output files grouped by platform & account
│ ├── instagram/
│ │ └── natgeo/
│ │ ├── Photos/ # Downloaded image assets
│ │ │ └── archive.txt # Deduplication tracking record
│ │ ├── Videos/ # Downloaded video assets
│ │ │ └── archive.txt
│ │ └── Stories/ # Downloaded story assets
│ │ └── archive.txt
│ └── tiktok/
│ └── example_user/
│ └── Videos/
│ └── archive.txt
├── cookies/ # Netscape-format cookie files (*_cookies.txt)
│ ├── instagram_cookies.txt
│ ├── tiktok_cookies.txt
│ ├── facebook_cookies.txt
│ └── x_cookies.txt
└── profiles/ # Batch target profile lists (*_profiles.txt)
├── instagram_profiles.txt
├── tiktok_profiles.txt
├── facebook_profiles.txt
└── x_profiles.txt
CLI Reference
nami download
Plan and execute downloads for individual URLs or profile lists.
nami download [URL ...] [OPTIONS]
| Parameter / Flag | Type | Description |
|---|---|---|
URL ... |
Positional | One or more content or profile URLs |
--profiles |
Flag | Batch download all targets listed in profiles_dir |
--platform |
Option | Filter or specify platform (instagram, tiktok, facebook, x) |
--media |
Option | Target media types: photos, videos, stories, highlights, all |
--json |
Flag | Output structured JSON for automation |
nami setup
Initialize directory layout and generate default configuration.
nami setup [OPTIONS]
| Flag | Type | Description |
|---|---|---|
--root <PATH> |
Option | Set root parent directory for the workspace |
--cookie-templates |
Flag | Generate template cookie files in cookies/ |
--json |
Flag | Output workspace initialization result as JSON |
nami doctor
Run read-only system diagnostic checks without network activity.
nami doctor [OPTIONS]
| Flag | Type | Description |
|---|---|---|
--json |
Flag | Emit structured health checks and remediation advice in JSON |
PASS config: Configuration file is valid
PASS python: Python 3.14.7 (>= 3.10 required)
PASS dependencies: All required packages are installed
PASS workspace: Base directory is writable
PASS browser: Brave is installed and unlocked
PASS cookies: Netscape cookies valid
PASS urllib3: Clean namespace (no conflicts)
PASS archive_locks: No stale locks detected
nami config
Manage persistent settings in ~/.nami/nami_config.json.
nami config show [--json]
nami config get <KEY> [--json]
nami config set <KEY> <VALUE> [--json]
nami config unset <KEY> [--json]
| Key | Valid Values | Description |
|---|---|---|
base_dir |
Valid path string | Root directory for downloaded media |
cookies_dir |
Valid path string | Directory containing Netscape cookie files |
profiles_dir |
Valid path string | Directory containing *_profiles.txt lists |
browser |
brave, chrome, edge, firefox |
Browser for automated cookie extraction |
user_agent |
Custom string | HTTP User-Agent string sent to engines |
timeout_seconds |
Integer (> 0) |
Subprocess execution deadline |
nami archive reset
Manage download tracking records (archive.txt) safely without data loss.
nami archive reset [OPTIONS]
| Flag | Type | Description |
|---|---|---|
--all |
Flag | Target all archives across all platforms |
--platform <NAME> |
Option | Target archives for a specific platform |
--target <KEY> |
Option | Target archives for a specific account/target |
--media <KIND> |
Option | Filter archives by media kind |
--dry-run |
Flag | Preview affected archives without modifying disk |
--delete |
Flag | Permanently delete matching archives (default: create .bak) |
--yes |
Flag | Confirm mutation without interactive prompt |
--json |
Flag | Output affected archive list in JSON |
Authentication & Cookies
1. Netscape Cookie Files
Place exported cookies into cookies_dir. Nami verifies that files contain genuine 7-column rows before mounting them to child engines:
instagram_cookies.txtorinstagram.com_cookies.txttiktok_cookies.txtortiktok.com_cookies.txtfacebook_cookies.txtorfacebook.com_cookies.txtx_cookies.txtorx.com_cookies.txt
2. Browser Database Session Extraction
For TikTok downloads without an explicit cookie file, Nami extracts session tokens directly from local browser stores (brave, chrome, edge, firefox).
Failure Taxonomy & Retry Policy
failures.py maps engine stderr diagnostics into typed FailureKind categories:
| Failure Category | Classification Criteria | Recovery Strategy |
|---|---|---|
FailureKind.AUTH |
HTTP 401, login required, session expired | 1 anonymous retry if credentials were provided |
FailureKind.COOKIE |
Corrupt or undecryptable cookie file | 1 anonymous retry if credentials were provided |
FailureKind.RATE_LIMIT |
HTTP 429, temporary platform block | Terminate immediately without cross-engine churn |
FailureKind.NETWORK |
Connection reset, TLS failure, DNS timeout | Up to 3 attempts with exponential jittered backoff |
FailureKind.EXTRACTOR |
Route failure, extractor deprecation | 1 retry on secondary engine (yt-dlp) |
FailureKind.NOT_FOUND |
HTTP 404, user not found, private media | Stop immediately with Outcome.NO_RESULTS |
FailureKind.DEPENDENCY |
Missing binary or unimportable module | Stop immediately |
FailureKind.TIMEOUT |
Subprocess wall-clock deadline exceeded | Up to 3 attempts with exponential backoff |
FailureKind.LOCKED |
Archive lock contention or account challenge | Stop immediately |
Deterministic Exit Codes
| Exit Code | Identifier | Meaning |
|---|---|---|
0 |
SUCCESS |
All plan steps downloaded or already up to date |
1 |
FAILED |
Unrecoverable error occurred on one or more operations |
2 |
INVALID |
Malformed CLI syntax, invalid URL, or corrupted configuration |
3 |
PARTIAL / WARN |
Mixed outcomes, unsupported operations, or doctor warnings |
4 |
NO_RESULTS |
Extractor ran successfully but found zero downloadable items |
130 |
CANCELLED |
Execution interrupted via SIGINT (Ctrl+C) |
Configuration & Environment Variables
| Setting Key | Environment Variable | Default Value | Description |
|---|---|---|---|
base_dir |
NAMI_BASE_DIR |
~/Nami/downloads |
Root directory for downloads |
cookies_dir |
NAMI_COOKIES_DIR |
~/Nami/cookies |
Directory for Netscape cookies |
profiles_dir |
NAMI_PROFILES_DIR |
~/Nami/profiles |
Directory for target profile lists |
browser |
NAMI_BROWSER |
brave |
Browser for automated cookie discovery |
user_agent |
NAMI_USER_AGENT |
Chrome Default | HTTP User-Agent header |
timeout_seconds |
NAMI_TIMEOUT_SECONDS |
1800 |
Process execution deadline in seconds |
| — | NAMI_THEME |
auto |
Terminal styling theme (dark, light, auto) |
| — | NAMI_SKIP_ENV_CHECK |
0 |
Set to 1 to bypass startup environment checks |
Development & Verification
Local Environment Setup
git clone https://github.com/OpenSelena/nami.git
cd nami
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
Verification Commands
# Run pytest unit test suite
python -m pytest
# Run Ruff linter and code formatter checks
python -m ruff check src tests
python -m ruff format --check src tests
# Verify package build & metadata
python -m build
python -m twine check dist/*
License
Distributed under the MIT License. Developed and maintained by Igect under OpenSelena.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nami-5.0.4.tar.gz.
File metadata
- Download URL: nami-5.0.4.tar.gz
- Upload date:
- Size: 69.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b8dbad02d8294a8fefb4d04021a30953f5008f67187014b9d6286a46c9f6321
|
|
| MD5 |
33efd6153a90e1d0045f68cc0fadd86b
|
|
| BLAKE2b-256 |
62fc61c25302451906b19507a28da61b883c98b8157b1c74d8eed001f399af38
|
Provenance
The following attestation bundles were made for nami-5.0.4.tar.gz:
Publisher:
publish.yml on OpenSelena/nami
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nami-5.0.4.tar.gz -
Subject digest:
1b8dbad02d8294a8fefb4d04021a30953f5008f67187014b9d6286a46c9f6321 - Sigstore transparency entry: 2568663029
- Sigstore integration time:
-
Permalink:
OpenSelena/nami@93f24dbf79b4c3ffe423fb6a55d33a363d8a80fd -
Branch / Tag:
refs/tags/v5.0.4 - Owner: https://github.com/OpenSelena
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@93f24dbf79b4c3ffe423fb6a55d33a363d8a80fd -
Trigger Event:
release
-
Statement type:
File details
Details for the file nami-5.0.4-py3-none-any.whl.
File metadata
- Download URL: nami-5.0.4-py3-none-any.whl
- Upload date:
- Size: 55.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4bd1ca16b3e84971842c0d3d792084c1c7af5022c2ea1c9804c2162b178a7c3f
|
|
| MD5 |
beb8522d65eed84dcec84d6356d15571
|
|
| BLAKE2b-256 |
f46942be1357785a44b6fa7816a01a0e25d5ff99a4559e558fddde9987989bcf
|
Provenance
The following attestation bundles were made for nami-5.0.4-py3-none-any.whl:
Publisher:
publish.yml on OpenSelena/nami
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nami-5.0.4-py3-none-any.whl -
Subject digest:
4bd1ca16b3e84971842c0d3d792084c1c7af5022c2ea1c9804c2162b178a7c3f - Sigstore transparency entry: 2568663068
- Sigstore integration time:
-
Permalink:
OpenSelena/nami@93f24dbf79b4c3ffe423fb6a55d33a363d8a80fd -
Branch / Tag:
refs/tags/v5.0.4 - Owner: https://github.com/OpenSelena
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@93f24dbf79b4c3ffe423fb6a55d33a363d8a80fd -
Trigger Event:
release
-
Statement type: