Nami coordinates gallery-dl and yt-dlp through a pure functional planning core and isolated execution shell. Engineered for batch extraction, atomic deduplication, classified retry loops, and headless pipelines.
Platform Matrix · Architecture · Installation · Quickstart · CLI Reference · Configuration · Diagnostics
Architecture
Nami separates pure domain logic from side effects using narrow module seams.
|
Dual-Engine Routing — Directs photos, stories, and highlights to |
Atomic Containment & Locking — Per-target PID-keyed |
|
Pure Functional Planning — Planning core converts inputs to deterministic |
Universal Invocation — Run interactively via Rich terminal UI or headlessly in CI/CD automation using |
Data flow
flowchart TD
subgraph Core ["Pure Domain Core (No Side Effects)"]
A[Target URLs / Profiles] -->|parse_target| B[targets.py]
B -->|build_plan| C[planner.py]
end
subgraph Shell ["Stateful Imperative Shell"]
D{NamiService Orchestrator}
D -->|Lock Archive| E[archive.py]
D -->|Execute Command| F[process.py]
F --> G[gallery-dl / yt-dlp]
F -->|Capture Output| H[failures.py]
H -->|Evaluate Decision| I[retry.py]
end
C --> D
D --> J[BatchResult: Exit Code & JSON]
Module seams
| Module | Public interface | Encapsulated complexity |
|---|---|---|
targets.py |
parse_target(), safe_target_dir(), resolve_target_endpoints() |
URL regex decomposition, host mapping, path traversal prevention, endpoint derivation |
planner.py |
build_plan(DownloadRequest) -> tuple[PlanStep, ...] |
Side-effect-free step generation, media kind expansion, destination path mapping |
archive.py |
ArchiveLock, discover_archives(), reset_archives() |
Non-blocking PID-keyed locking, stale lock identification, .bak file generation |
process.py |
SubprocessRunner.run(CommandSpec) -> CommandResult |
Zero-shell execution (shell=False), credential sanitization, cross-platform process tree termination |
failures.py |
classify_failure(), failure_message() |
Error stream parsing, diagnostic precedence ranking, typed FailureKind categorization |
retry.py |
RetryPolicy.decide() -> RetryDecision |
Bounded exponential backoff with deterministic pseudo-random jitter |
Platform Support Matrix
| Platform | Photos & Posts | Videos & Reels | Stories | Highlights | Authentication | Primary Backend |
|---|---|---|---|---|---|---|
| Supported | Supported | Supported | Supported | Netscape Cookie / Anonymous | gallery-dl / yt-dlp (Fallback) |
|
| TikTok | Limited | Supported | — | — | Browser DB / Netscape Cookie | yt-dlp / gallery-dl |
| Limited | Supported | — | — | Netscape Cookie / Anonymous | gallery-dl / yt-dlp |
|
| X (Twitter) | Limited | Supported | — | — | Netscape Cookie / Anonymous | gallery-dl / yt-dlp |
[!NOTE] Unsupported platform/media combinations return structured
Outcome.UNSUPPORTEDrecords and exit with status3.
Installation
Requires Python 3.10 or newer.
# Install from PyPI
python -m pip install nami
# Upgrade existing install
python -m pip install --upgrade nami
Quickstart
Interactive mode
Run nami (or python -m nami if Python Scripts directory is not on system PATH):
nami
╭─ Nami ───────────────────────────────────────────────────────────── v5.0.6 ─╮
│ 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 │
╰─────────────────────────────────────────────────────────────────────────────╯
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 layout
Nami/
├── downloads/ # Output directory organized by platform & account
│ ├── instagram/
│ │ └── natgeo/
│ │ ├── Photos/
│ │ │ └── archive.txt # Deduplication ledger
│ │ ├── Videos/
│ │ │ └── archive.txt
│ │ └── Stories/
│ │ └── archive.txt
│ └── tiktok/
│ └── example_user/
│ └── Videos/
│ └── archive.txt
├── cookies/ # Netscape-format cookie files
│ ├── instagram_cookies.txt
│ ├── tiktok_cookies.txt
│ ├── facebook_cookies.txt
│ └── x_cookies.txt
└── profiles/ # Target profile lists
├── 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]
| Flag | Type | Description |
|---|---|---|
URL ... |
Positional | One or more content or profile URLs |
--profiles |
Flag | Batch download all targets listed in profiles_dir |
--platform |
Option | Disambiguate platform (instagram, tiktok, facebook, x) |
--media |
Option | Comma-separated kinds: photos, videos, stories, highlights, all |
--json |
Flag | Output structured JSON result for automated pipelines |
nami setup
Initialize directory layout and write configuration.
nami setup [OPTIONS]
| Flag | Type | Description |
|---|---|---|
--root <PATH> |
Option | Target parent directory for the workspace |
--cookie-templates |
Flag | Create empty Netscape cookie templates in cookies/ |
--json |
Flag | Output initialization result as JSON |
nami doctor
Run read-only system diagnostic checks without network access.
nami doctor [OPTIONS]
| Flag | Type | Description |
|---|---|---|
--json |
Flag | Output structured health checks and remediation steps in JSON |
Example output:
PASS config: Configuration loaded successfully
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 |
Path string | Root directory for downloaded media |
cookies_dir |
Path string | Directory containing Netscape cookie files |
profiles_dir |
Path string | Directory containing *_profiles.txt lists |
browser |
brave, chrome, edge, firefox |
Browser for automated cookie extraction |
user_agent |
String | HTTP User-Agent header passed to child engines |
timeout_seconds |
Integer (> 0) |
Subprocess execution deadline in seconds |
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 | Filter archives by platform |
--target <KEY> |
Option | Filter archives by target account |
--media <KIND> |
Option | Filter archives by media kind |
--dry-run |
Flag | Preview affected archives without modifying disk |
--delete |
Flag | Permanently delete matching archives (default: creates .bak) |
--yes |
Flag | Confirm action without interactive prompt |
--json |
Flag | Output affected archive paths in JSON |
Authentication & Cookies
Netscape cookie files
Save exported cookies into cookies_dir. Nami verifies that files contain genuine 7-column rows before passing 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
Browser database extraction
For TikTok downloads without a cookie file, Nami extracts session tokens from local browser databases (brave, chrome, edge, firefox).
Failure Taxonomy & Recovery Matrix
failures.py maps subprocess output to deterministic FailureKind categories:
| Failure Kind | Diagnostic Indicator | Recovery Strategy |
|---|---|---|
AUTH |
HTTP 401, login required | 1 anonymous retry if credentials were provided |
COOKIE |
Corrupt or undecryptable cookie file | 1 anonymous retry if credentials were provided |
RATE_LIMIT |
HTTP 429, temporary platform throttle | Stop immediately without cross-engine churn |
NETWORK |
Connection reset, TLS error, DNS timeout | Up to 3 attempts with exponential jittered backoff |
EXTRACTOR |
Route failure, extractor deprecation | 1 retry on alternate engine (yt-dlp) |
NOT_FOUND |
HTTP 404, account deleted, private media | Stop immediately with Outcome.NO_RESULTS |
DEPENDENCY |
Missing binary or unimportable module | Stop immediately |
TIMEOUT |
Subprocess deadline exceeded | Up to 3 attempts with exponential backoff |
LOCKED |
Archive lock contention or account challenge | Stop immediately |
Deterministic Exit Codes
| Code | Outcome | Meaning |
|---|---|---|
0 |
SUCCESS |
All operations 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 |
Mixed outcomes, unsupported operations, or doctor warnings |
4 |
NO_RESULTS |
Extractor completed successfully but found zero downloadable items |
130 |
CANCELLED |
Execution interrupted via SIGINT (Ctrl+C) |
Configuration & Environment Variables
| Setting Key | Environment Variable | Default | 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 extraction |
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.6.tar.gz.
File metadata
- Download URL: nami-5.0.6.tar.gz
- Upload date:
- Size: 71.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e7aa7af72c503f2bd8a9bf108764db608d2ba4cb8fac27ea0688be6ca0a99e75
|
|
| MD5 |
a815d6a1d8d4fe80ff1c2d5f2d89f1cf
|
|
| BLAKE2b-256 |
cac3fb4ed2601087228fa22ba5d63085917d7dd9c2e31df243912aba17137296
|
Provenance
The following attestation bundles were made for nami-5.0.6.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.6.tar.gz -
Subject digest:
e7aa7af72c503f2bd8a9bf108764db608d2ba4cb8fac27ea0688be6ca0a99e75 - Sigstore transparency entry: 2569614871
- Sigstore integration time:
-
Permalink:
OpenSelena/nami@278a3d47d67d1f346302984e8397050c5b981950 -
Branch / Tag:
refs/tags/v5.0.6 - Owner: https://github.com/OpenSelena
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@278a3d47d67d1f346302984e8397050c5b981950 -
Trigger Event:
release
-
Statement type:
File details
Details for the file nami-5.0.6-py3-none-any.whl.
File metadata
- Download URL: nami-5.0.6-py3-none-any.whl
- Upload date:
- Size: 56.3 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 |
e3cb1288e655a4e4904d160e879f1ee33299e7c4e8e2fc10a36864ff2a139c88
|
|
| MD5 |
754c8a970ae19ef44dc15cd926f1b986
|
|
| BLAKE2b-256 |
e5de7e7821df36d588f0437532121cc93d0caf7187317a3e4b82aeec405a6b3c
|
Provenance
The following attestation bundles were made for nami-5.0.6-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.6-py3-none-any.whl -
Subject digest:
e3cb1288e655a4e4904d160e879f1ee33299e7c4e8e2fc10a36864ff2a139c88 - Sigstore transparency entry: 2569614874
- Sigstore integration time:
-
Permalink:
OpenSelena/nami@278a3d47d67d1f346302984e8397050c5b981950 -
Branch / Tag:
refs/tags/v5.0.6 - Owner: https://github.com/OpenSelena
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@278a3d47d67d1f346302984e8397050c5b981950 -
Trigger Event:
release
-
Statement type: