CLI Reference

April 29, 2026 · View on GitHub

media-archivist is a command-line tool for indexing and managing media metadata across five sources: YouTube, YouTube Music, Internet Archive, Bandcamp, and SoundCloud.

Global Options

All subcommands accept these options:

FlagTypeDefaultDescription
--versionN/AN/AShow version and exit
-v, --verboseflagoffEnable debug logging

Database Target (Required for all data subcommands)

Exactly one of these must be provided:

FlagFormatUse Case
--db NAMEstringAuto-place under XDG at ~/.local/share/media_archivist/<NAME>.json (recommended for shared databases)
--db-file PATHpathExplicit file location (recommended for datasets you commit alongside scripts)

Backend Selection (Mutually Exclusive, Default: YouTube)

FlagBackendArchivistNotes
(none)YouTubeYoutubeArchivistDefault; channels, playlists, search results
--musicYouTube MusicYoutubeMusicArchivistRich track metadata: artist, album, year, explicit
--iaInternet ArchiveIAArchivistStreaming collections, video files
--bandcampBandcampBandcampArchivistRequires pip install py_bandcamp
--soundcloudSoundCloudSoundCloudArchivistRequires pip install nuvem_de_som

Common Filters

These apply to add, urls, list, export, monitor:

FlagTypeDefaultDescription
--require KWstring(none)Only index entries whose title contains all these keywords (repeatable)
--blacklist KWstring(none)Skip entries whose title contains any of these keywords (repeatable)
--min-duration SECONDSint-1Minimum duration in seconds (only for backends that expose length)
--skip-explicitflagoff(YouTube Music only) skip tracks flagged explicit
--only-audioflagoff(YouTube Music only) keep only audio-only tracks (no music videos)

View Flags

These flags apply to urls, list, export — they activate the read-side Index and canonical view:

FlagTypeDefaultDescription
--canonicalflagoffUse the canonical MediaEntry view (including external IDs, canonical status)
--where EXPRstring(none)Filter expression (e.g., duration>180 and source=="youtube_music") — see WHERE Language reference
--source NAMEstring(none)Keep only entries from this source (youtube, youtube_music, bandcamp, soundcloud, internet_archive)
--has-streamflagNoneKeep only entries with a resolved stream URL
--no-streamflagNoneKeep only entries without a stream URL
--explicitflagNone(Canonical view only) keep only explicit-flagged tracks
--no-explicitflagNone(Canonical view only) drop explicit-flagged tracks

Subcommands

add

Add one or more URLs to the database.

media-archivist add URL [URL ...]

Options:

FlagDescription
--db, --db-fileDatabase target (required)
--<backend>Backend selector (optional; default: youtube)
--require, --blacklist, --min-durationFilters

Exit Codes:

  • 0 — Success
  • 1 — Validation error (no DB target)

Examples:

# Add a YouTube channel
media-archivist --db-file talks.json add https://www.youtube.com/@SomeChannel

# Add multiple URLs with a keyword filter
media-archivist --db-file music.json --music add \
  https://music.youtube.com/browse/MPREb_xxx \
  https://music.youtube.com/playlist?list=PLyyy

# Add only tracks longer than 3 minutes
media-archivist --db-file tracks.json --music --min-duration 180 add \
  "relaxing ambient music"

urls

Print stored URLs (one per line). Output is suitable for piping to yt-dlp -a -.

media-archivist urls [--grep PATTERN] [--limit N]

Options:

FlagTypeDefaultDescription
--grep PATTERNstring(none)Filter by substring in title (case-insensitive)
--limit Nint0Emit at most N URLs (0 = unlimited)
View flags——--canonical, --where, --source, --has-stream, --no-stream

Exit Codes:

  • 0 — Success
  • 1 — --where expression error

Examples:

# Dump all URLs to yt-dlp
media-archivist --db-file talks.json urls | yt-dlp -a -

# Download only "podcast" entries
media-archivist --db-file talks.json urls --grep podcast | yt-dlp -a -

# List only YouTube Music URLs with valid streams
media-archivist --db-file db.json urls --canonical --source youtube_music --has-stream

# Complex filter: music entries longer than 5 minutes from Bandcamp
media-archivist --db-file db.json urls --canonical --where "source=='bandcamp' and duration>300"

list

List entries in human-readable format (title, tab, URL) or JSON.

media-archivist list [--grep PATTERN] [--limit N] [--json]

Options:

FlagTypeDefaultDescription
--grep PATTERNstring(none)Filter by substring in title
--limit Nint0Emit at most N entries
--jsonflagoffEmit JSON array instead of tab-separated
View flags——--canonical, --where, --source, --has-stream, --no-stream, --explicit, --no-explicit

Exit Codes:

  • 0 — Success
  • 1 — --where expression error

Examples:

# List all entries
media-archivist --db-file db.json list

# List with JSON output
media-archivist --db-file db.json list --json

# Entries from specific sources
media-archivist --db-file db.json list --canonical --source bandcamp --limit 10

# Non-explicit music tracks
media-archivist --db-file db.json list --canonical --no-explicit --source youtube_music

dump

Dump the entire raw database as JSON (pretty-printed).

media-archivist dump

Options:

FlagDescription
--db, --db-fileDatabase target (required)

Exit Codes:

  • 0 — Success

Examples:

# Backup the database
media-archivist --db-file db.json dump > backup.json

# Inspect raw structure
media-archivist --db-file db.json dump | jq '._meta'

export

Export entries as JSON, JSONL, CSV, or plain text with optional field projection.

media-archivist export [--format FORMAT] [--fields A,B,C] [--output PATH]

Options:

FlagTypeDefaultDescription
--format FORMATenumjsonlOne of: json, jsonl, csv, txt
--fields A,B,Cstring(all)Comma-separated field names to project (default: all)
--output PATHpathstdoutWrite to PATH instead of stdout
--grep PATTERNstring(none)Filter by title substring
--limit Nint0Emit at most N rows
View flags——--canonical, --where, --source, --has-stream

Default Fields (for CSV when --fields not specified):

videoId, title, url, thumbnail, published, views, is_live, tags, description, playlist

Exit Codes:

  • 0 — Success
  • 1 — Invalid format
  • 2 — File write error

Examples:

# Export as JSONL (one entry per line)
media-archivist --db-file db.json export --format jsonl > export.jsonl

# Export as CSV with selected columns
media-archivist --db-file db.json export --format csv \
  --fields videoId,title,url,published > dataset.csv

# Export canonical records
media-archivist --db-file db.json export --format json --canonical -o canonical.json

# Export only long-form content
media-archivist --db-file db.json export --canonical --where "duration>1800" -o long_form.jsonl

import

Load entries from an external JSON or JSONL file into the database.

media-archivist import PATH [--overwrite]

Options:

FlagTypeDescription
PATHpath.json (dict or list) or .jsonl input file (required)
--db, --db-filestringDatabase target (required)
--overwriteflagReplace existing entries with the same URL

File Format:

  • .json: Object mapping URLs to entries, or list of entries (each with a url field)
  • .jsonl: One JSON object per line, each with a url field

Exit Codes:

  • 0 — Success

Examples:

# Import from a JSON object
media-archivist --db-file db.json import backup.json

# Import from JSONL
media-archivist --db-file db.json import export.jsonl

# Import and overwrite duplicates
media-archivist --db-file db.json import new_data.json --overwrite

merge

Merge multiple source database files into the destination database.

media-archivist merge SOURCE [SOURCE ...] [--overwrite]

Options:

FlagTypeDescription
SOURCEpathSource .json DB paths (required, at least one)
--db, --db-filestringDestination database target (required)
--overwriteflagReplace existing entries with the same URL

Exit Codes:

  • 0 — Success

Examples:

# Merge two databases
media-archivist --db-file combined.json merge db1.json db2.json

# Merge with overwrite
media-archivist --db-file prod.json merge staging1.json staging2.json --overwrite

stats

Print dataset statistics (total entries, live status, per-playlist breakdown, field coverage).

media-archivist stats

Options:

FlagDescription
--db, --db-fileDatabase target (required)

Output:

JSON object with keys:

  • total — Total number of entries
  • live — Number of entries flagged as live
  • playlists — Object mapping playlist name to count
  • field_coverage — Object mapping field name to count of non-null entries

Exit Codes:

  • 0 — Success

Examples:

media-archivist --db-file db.json stats

# Output:
# {
#   "total": 250,
#   "live": 5,
#   "playlists": {
#     "My Playlist": 50,
#     "Another": 30
#   },
#   "field_coverage": {
#     "videoId": 250,
#     "title": 248,
#     "url": 250,
#     "duration": 200,
#     ...
#   }
# }

prune

Remove entries by various criteria.

media-archivist prune [--unavailable] [--below MINUTES] [--missing FIELD ...]

Options:

FlagTypeDescription
--unavailableflagDrop entries that no longer resolve (oEmbed probe for YouTube)
--below MINUTESintDrop entries shorter than MINUTES
--missing FIELDstringDrop entries missing FIELD (repeatable)
--blacklist KWstring(inherited) skip entries with this keyword in title

Validation:

At least one of --unavailable, --below, --missing, or --blacklist must be specified.

Exit Codes:

  • 0 — Success

Examples:

# Remove videos that no longer exist
media-archivist --db-file db.json prune --unavailable

# Remove entries shorter than 10 minutes
media-archivist --db-file db.json prune --below 10

# Remove entries missing a title
media-archivist --db-file db.json prune --missing title

# Remove short clips and missing metadata
media-archivist --db-file db.json prune --below 5 --missing duration

bootstrap

Seed an empty database from a remote JSON dump URL.

media-archivist bootstrap URL

Options:

FlagTypeDescription
URLstringRemote URL to a JSON file (required)
--db, --db-filestringDatabase target (required)

Notes:

Only supported by YoutubeArchivist and YoutubeMonitor; other backends return an error.

Exit Codes:

  • 0 — Success
  • 1 — Backend doesn't support bootstrap

Examples:

media-archivist --db-file db.json bootstrap https://example.com/archive.json

Compute fingerprint groups and write the <db>.links.json sidecar.

media-archivist link [--duration-tolerance SECONDS]

Options:

FlagTypeDefaultDescription
--duration-tolerance SECONDSfloat2.0Seconds of duration mismatch tolerated within a fingerprint group
--db, --db-filestring—Database target (required)

Output:

Writes <db>.links.json with structure:

{
  "<fingerprint>": ["id1", "id2", ...],
  "<fingerprint>:1": ["id3", "id4", ...],
  ...
}

Entries in the same group (by fingerprint) are likely duplicates across sources.

Exit Codes:

  • 0 — Success

Examples:

media-archivist --db-file db.json link --duration-tolerance 2.0

dedupe

Read view + links and emit a deduped canonical JSONL.

media-archivist dedupe --output PATH [--prefer A,B,C]

Options:

FlagTypeDefaultDescription
--output PATHpath—Output JSONL path (required)
--prefer A,B,Cstringbandcamp,internet_archive,youtube_music,soundcloud,youtubeComma-separated source preference order (winners first)
--duration-tolerance SECONDSfloat2.0Duration tolerance for link validation
--db, --db-filestring—Database target (required)

Output:

One canonical JSONL row per deduplicated group, with fields from the preferred source.

Exit Codes:

  • 0 — Success

Examples:

media-archivist --db-file db.json dedupe --output canonical.jsonl

# Custom preference order
media-archivist --db-file db.json dedupe --output canonical.jsonl \
  --prefer internet_archive,youtube_music,youtube

providers

List built-in metadata providers and their active status.

media-archivist providers

Options:

None. This subcommand doesn't require a DB target.

Output:

JSON array with entries:

[
  {
    "name": "musicbrainz",
    "active": true,
    "media": ["music"]
  },
  {
    "name": "tmdb",
    "active": false,
    "media": ["movie", "tv"]
  },
  ...
]

Exit Codes:

  • 0 — Success

Examples:

media-archivist providers
media-archivist providers | jq '.[] | select(.active)'

canonicalize

Run metadata providers against the database and update canonical/quarantine sidecars.

media-archivist canonicalize [--providers NAME ...] [--no-stamp]

Options:

FlagTypeDefaultDescription
--providers NAMEstring(all active)Restrict to this provider (repeatable); if not specified, runs all active providers
--no-stampflagoffDon't write _meta.canonical_id back to rows in the database
--db, --db-filestring—Database target (required)

Side Effects:

  • Writes/updates <db>.canonical.json with deduplicated records and external IDs
  • Writes/updates <db>.quarantine.json with conflicting entries
  • If --no-stamp is not set, stamps each row's _meta.canonical_id and _meta.canonical_status

Exit Codes:

  • 0 — Success

Examples:

# Run all active providers
media-archivist --db-file db.json canonicalize

# Run only MusicBrainz and Wikidata
media-archivist --db-file db.json canonicalize --providers musicbrainz --providers wikidata

# Run without modifying the source DB
media-archivist --db-file db.json canonicalize --no-stamp

quarantine-list

Dump the quarantine sidecar as JSON.

media-archivist quarantine-list

Options:

FlagDescription
--db, --db-fileDatabase target (required)

Output:

JSON with structure:

{
  "version": 1,
  "entries": {
    "<row_id>": {
      "row_id": "...",
      "candidate_canonical_id": "...",
      "conflicts": [
        {"signal": "title", "ours": "...", "theirs": "..."}
      ],
      "proposed_signals": {...},
      "first_seen": "2025-01-15T...",
      "last_seen": "2025-01-15T..."
    }
  }
}

Exit Codes:

  • 0 — Success

Examples:

media-archivist --db-file db.json quarantine-list | jq '.entries | keys'

quarantine-resolve

Accept a quarantined row and link it to a canonical record.

media-archivist quarantine-resolve --row-id ID [--canonical-id CANONICAL_ID]

Options:

FlagTypeDescription
--row-id IDstringRow ID to resolve (required)
--canonical-id CANONICAL_IDstringLink to this existing canonical_id; if omitted, allocate new from proposed signals
--db, --db-filestringDatabase target (required)

Side Effects:

  • Removes the entry from the quarantine sidecar
  • Stamps the row with the canonical_id and status "matched"

Exit Codes:

  • 0 — Success
  • 1 — row_id not found in quarantine

Examples:

# Resolve to an existing canonical record
media-archivist --db-file db.json quarantine-resolve \
  --row-id abc123def456 \
  --canonical-id canonical_xyz

# Allocate a new canonical ID
media-archivist --db-file db.json quarantine-resolve --row-id abc123def456

quarantine-reject

Reject a proposal and force a fresh canonical_id.

media-archivist quarantine-reject --row-id ID

Options:

FlagTypeDescription
--row-id IDstringRow ID to reject (required)
--db, --db-filestringDatabase target (required)

Side Effects:

  • Removes the entry from the quarantine sidecar
  • Allocates a fresh canonical_id and stamps the row with status "unmatched"

Exit Codes:

  • 0 — Success
  • 1 — row_id not found in quarantine

Examples:

media-archivist --db-file db.json quarantine-reject --row-id abc123def456

monitor

Background-poll URLs and keep the database in sync.

media-archivist monitor URL [URL ...] [--interval SECONDS]

Options:

FlagTypeDefaultDescription
URLstring—Channel, playlist, or artist URLs to monitor (required, at least one)
--interval SECONDSint120Seconds between syncs
--db, --db-filestring—Database target (required)
--require, --blacklist, --min-duration——Filters applied to each sync

Notes:

  • Not supported with --ia backend
  • Runs indefinitely; press Ctrl-C to stop
  • Logs sync events to stderr

Exit Codes:

  • 0 — Success or Ctrl-C
  • 1 — Backend error or validation error

Examples:

# Monitor a YouTube channel every 2 minutes
media-archivist --db-file db.json monitor https://www.youtube.com/@SomeChannel

# Monitor multiple playlists every 5 minutes
media-archivist --db-file music.json --music monitor \
  https://music.youtube.com/playlist?list=PLyyy \
  https://music.youtube.com/playlist?list=PLzzz \
  --interval 300

# Monitor with filters
media-archivist --db-file db.json monitor https://www.youtube.com/@Channel \
  --min-duration 600 --require "important keyword"

Exit Codes

CodeMeaning
0Success
1Validation error, missing argument, or command not supported
2File I/O error (e.g., bad output format)

Error Messages

Common errors and resolutions:

ErrorCauseResolution
error: pass --db NAME or --db-file PATHNo database targetSpecify exactly one of --db or --db-file
error: --where: <message>Invalid WHERE expressionCheck syntax; see WHERE Language reference
error: <backend> backend requires 'pip install <package>'Backend not installedInstall optional dependency
error: row_id <id> not in quarantineQuarantine operation on non-existent rowCheck the row_id against quarantine-list output