Skip to main content

Nami

An open-source CLI media downloader for Instagram, TikTok, Facebook, and X.

CI PyPI Version Python Version License: MIT

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.


Table of Contents


Supported Platforms

Platform Photos / Posts Videos / Reels Stories Highlights Authentication Primary Engine
Instagram Supported Supported Supported Supported Netscape Cookie / Anonymous gallery-dl (Photos/Stories) / yt-dlp (Video Fallback)
TikTok Limited by upstream Supported Unsupported Unsupported Browser DB / Netscape Cookie yt-dlp / gallery-dl
Facebook Limited by upstream Supported Unsupported Unsupported Netscape Cookie / Anonymous gallery-dl / yt-dlp
X (Twitter) Limited by upstream Supported Unsupported Unsupported Netscape Cookie / Anonymous gallery-dl / yt-dlp

Unsupported media/platform combinations return structured Outcome.UNSUPPORTED (Exit Code 3) rather than silent failure or fake success.


Architecture & Design Principles

  1. Pure Core, Imperative Shell:
    • targets.py: URL parsing, sanitization, canonical host mapping, and media endpoint expansion.
    • planner.py: Pure mapping from Target + MediaKind to deterministic PlanStep sequences without filesystem I/O.
    • retry.py: Stateless retry decision engine with exponential jittered backoff.
    • service.py: Orchestrator executing plan steps behind atomic file locks (archive.lock).
  2. Dual-Engine Routing (ADR-0001):
    • Photos, Instagram stories, and highlights route to gallery-dl.
    • Videos route to gallery-dl with automatic fallback to yt-dlp upon FailureKind.EXTRACTOR.
    • Network errors, rate limits, and authentication rejections retry on the same engine without inappropriate cross-engine churn.
  3. No Shell Invocations (shell=False):
    • All subprocess arguments are passed as discrete string arrays (argv) to prevent shell injection.
    • Process trees are tracked and terminated via process group signals (SIGTERM / SIGKILL on POSIX, taskkill /PID <PID> /T /F on Windows).
  4. Strict Path Containment:
    • Every destination folder is validated against base_dir using safe_target_dir() to prevent path traversal attacks.

Installation

Requires Python 3.10 or newer.

python -m pip install nami

Upgrade to latest release:

python -m pip install --upgrade nami

Quickstart

1. Interactive Terminal UI

Run nami without arguments:

nami
┌──────────────────────── Nami ────────────────────────┐
│ 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                              │
└──────────────────────────────────────────────────────┘

On first launch without configuration, Nami interactively guides setup and workspace initialization.

2. Command-Line Direct Invocations

# Download direct posts
nami download https://www.instagram.com/p/C_EXAMPLE/ https://x.com/OpenAI/status/1234567890

# Filter specific media kinds
nami download https://www.instagram.com/natgeo/ --media photos,videos

# Batch download profiles from configured text files
nami download --profiles --platform instagram --media stories,highlights

Workspace Layout

Nami/
├── downloads/
│   ├── instagram/
│   │   └── natgeo/
│   │       ├── Photos/
│   │       │   └── archive.txt
│   │       ├── Videos/
│   │       │   └── archive.txt
│   │       └── Stories/
│   │           └── archive.txt
│   └── tiktok/
│       └── example_user/
│           └── Videos/
├── cookies/
│   ├── instagram_cookies.txt
│   ├── tiktok_cookies.txt
│   ├── facebook_cookies.txt
│   └── x_cookies.txt
└── profiles/
    ├── instagram_profiles.txt
    ├── tiktok_profiles.txt
    ├── facebook_profiles.txt
    └── x_profiles.txt

CLI Reference

nami setup

Initializes configuration and workspace directories:

# Initialize workspace under specific root
nami setup --root /path/to/parent

# Create empty Netscape cookie template files
nami setup --root . --cookie-templates

# Machine-readable JSON output
nami setup --root . --json

nami download

Executes download planning and execution for URLs and profile lists:

nami download [URL ...] [OPTIONS]
Flag Type Description
URL ... Positional One or more direct content or profile URLs
--profiles Flag Read target URLs from profiles_dir/<platform>_profiles.txt
--platform Option Restrict or disambiguate platform (instagram, tiktok, facebook, x)
--media Option Comma-separated media kinds (photos, videos, stories, highlights, all)
--json Flag Output structured JSON result for automated pipelines

nami doctor

Runs local, read-only diagnostic checks without network I/O:

nami doctor
nami doctor --json

Checks executed:

  • Config JSON structure and permissions
  • Python version (>= 3.10)
  • Core module imports (rich, gallery_dl, yt_dlp)
  • Read/write access on base_dir, cookies_dir, and profiles_dir
  • Browser installation and process lock check (brave, chrome, edge, firefox)
  • Netscape cookie file syntax validation (minimum 7 valid columns)
  • Profile file accessibility and syntax validation
  • Namespace conflicts (urllib3_future, niquests)
  • Stale archive.lock files (> 1 hour old)

nami config

Inspects and updates persistent configuration (~/.nami/nami_config.json):

# Display all configuration
nami config show

# Get specific key
nami config get base_dir
nami config get browser

# Set key
nami config set browser chrome
nami config set timeout_seconds 3600

# Reset key to default
nami config unset browser

# JSON mode
nami config show --json

nami archive reset

Manages download tracking records (archive.txt) safely:

# Dry run preview
nami archive reset --all --dry-run

# Back up archives for target (creates .bak files)
nami archive reset --platform instagram --target natgeo --yes

# Reset specific media kind
nami archive reset --platform instagram --target natgeo --media stories --yes

# Delete matching archives permanently
nami archive reset --platform tiktok --target creator --delete --yes

Authentication

Export cookies from your browser using a Netscape-compatible extension and save them into cookies_dir:

  • instagram_cookies.txt or instagram.com_cookies.txt
  • tiktok_cookies.txt or tiktok.com_cookies.txt
  • facebook_cookies.txt or facebook.com_cookies.txt
  • x_cookies.txt, x.com_cookies.txt, or twitter_cookies.txt

Nami validates that cookie files contain valid 7-column rows before passing them to engines.

When downloading from TikTok without an explicit cookie file, Nami attempts direct cookie extraction from the configured browser (brave, chrome, edge, firefox).


Failure Classification & Retry Policy

failures.py maps error logs to typed FailureKind categories:

Failure Kind Description Retry Action
FailureKind.AUTH HTTP 401, login required 1 anonymous retry if credentials were supplied
FailureKind.COOKIE Cookie decryption or file error 1 anonymous retry if credentials were supplied
FailureKind.RATE_LIMIT HTTP 429, too many requests Stop immediately (no retry)
FailureKind.NETWORK DNS reset, SSL, connection timeout Up to 3 attempts with exponential jittered backoff
FailureKind.EXTRACTOR Unsupported route, broken extractor 1 retry on alternate engine (yt-dlp)
FailureKind.NOT_FOUND HTTP 404, account deleted Stop immediately (Outcome.NO_RESULTS)
FailureKind.DEPENDENCY Missing binary or module Stop immediately
FailureKind.TIMEOUT Subprocess deadline exceeded Up to 3 attempts with backoff
FailureKind.LOCKED Checkpoint or archive lock busy Stop immediately

Exit Codes

Code Outcome Description
0 SUCCESS All items downloaded or already up to date
1 FAILED Unrecoverable failure across attempts
2 INVALID Malformed CLI arguments, invalid target URLs, or corrupt config
3 PARTIAL / WARN Mixed batch outcomes, unsupported operations, or doctor warnings
4 NO_RESULTS Extractor ran cleanly but found zero downloadable items
130 CANCELLED Execution interrupted via SIGINT (Ctrl+C)

Development & Testing

Setup Environment

git clone https://github.com/OpenSelena/nami.git
cd nami
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"

Running Test Suite

# Run pytest
python -m pytest

# Run Ruff linter and formatter
python -m ruff check src tests
python -m ruff format --check src tests

# Build distribution package
python -m build

License

Distributed under the MIT License. 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

nami-5.0.3.tar.gz (67.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nami-5.0.3-py3-none-any.whl (53.9 kB view details)

Uploaded Python 3

File details

Details for the file nami-5.0.3.tar.gz.

File metadata

  • Download URL: nami-5.0.3.tar.gz
  • Upload date:
  • Size: 67.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for nami-5.0.3.tar.gz
Algorithm Hash digest
SHA256 477b711bb4d63d45f6dd5cd8d2677f7032153d325a5a50a8b6552a5b2b24c643
MD5 02ecc0271f9a4d6b47619a5a92163fd7
BLAKE2b-256 4e89476da99e6b4ac95cf68a9564bdbaae06172847a24e1f5323575bcc739065

See more details on using hashes here.

Provenance

The following attestation bundles were made for nami-5.0.3.tar.gz:

Publisher: publish.yml on OpenSelena/nami

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nami-5.0.3-py3-none-any.whl.

File metadata

  • Download URL: nami-5.0.3-py3-none-any.whl
  • Upload date:
  • Size: 53.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for nami-5.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 1acaa4f0fc49c5986cf3b21cd11c9c6a0d8838b59458c37a7b7cb745e81ac9a6
MD5 ea945cbfe425e4e25109540121d00dcc
BLAKE2b-256 a061c89abc5fe55e3abc9d5dbdb80b296f9f24668f0c9d4d8e6d8b38398b6697

See more details on using hashes here.

Provenance

The following attestation bundles were made for nami-5.0.3-py3-none-any.whl:

Publisher: publish.yml on OpenSelena/nami

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

5.0.6

2 files

5.0.4

2 files

This release

5.0.3 This release

2 files

5.0.2

2 files

5.0.1

2 files

5.0.0

2 files

4.0.0

2 files

3.0.4

2 files

3.0.3

2 files

3.0.2

2 files

3.0.1

2 files

3.0.0

2 files

2.5.2

2 files

2.5.1

2 files

2.5.0

2 files

2.4.1

2 files

2.4.0

2 files

2.3.9

2 files

2.3.8

2 files

2.3.7

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page