movie-cli-v7

command module
v0.0.0-...-a78dc5a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 1, 2026 License: MIT Imports: 1 Imported by: 0

README ΒΆ

Movie CLI icon

🎬 Movie CLI

Personal movie & TV show library manager β€” from the terminal

CI Release GitHub Release Downloads Go Platform SQLite Go Report Card Last commit Code size Open issues Stars PRs welcome License

Scan folders, clean filenames, fetch TMDb metadata, organize files, and track your collection.


Movie CLI scanning a library in real time

↑ One command, one binary β€” scan, match against TMDb, and organize your whole library.



πŸš€ Install in 10 seconds β€” anyone, any OS:

πŸͺŸ Windows Β· PowerShell
irm https://raw.githubusercontent.com/alimtvnetwork/movie-cli-v7/main/get.ps1 | iex
🐧 macOS · Linux · Bash
curl -fsSL https://raw.githubusercontent.com/alimtvnetwork/movie-cli-v7/main/get.sh | bash

Auto-detects your OS & architecture Β· Installs the latest pre-built binary Β· Falls back to a source build if no release is published Β· See Installation for flags, pinned versions, and verification.


πŸ’‘ Why this exists

If you've ever ripped years of old DVDs to a hard drive β€” or just collected movies the slow way β€” you know the pain: a folder full of cryptic filenames like MV_0432.avi or the.matrix.1999.1080p.x264.mkv, no posters, no ratings, no idea what's actually worth watching tonight.

Movie CLI was built out of that frustration. It scans your existing folders, cleans up the messy release names, matches each file against TMDb, and gives you a real library on your own disk β€” with titles, posters, ratings, genres, cast, runtime, and watch history β€” without uploading anything to a cloud service. Your files stay where they are; you just finally know what you have.


✨ Highlights

  • πŸ” Smart scan β€” recursively walks folders, cleans messy release names, and matches them against TMDb
  • πŸ–ΌοΈ Posters & metadata β€” automatic thumbnail downloads, ratings, genres, cast, runtime
  • πŸ“¦ Single binary β€” one statically-linked Go executable, no runtime, no dependencies
  • πŸ—‚οΈ SQLite (WAL) β€” fast, durable, zero-config local database in ./data/movie.db
  • ↩️ Undo / redo β€” every move, rename, scan, and delete is reversible
  • 🌐 REST API + web UI β€” movie rest --open launches a local dashboard
  • πŸ› οΈ Self-updating β€” movie update pulls, rebuilds, and hands off in-place
  • πŸ”’ Cross-platform β€” Windows, Linux, macOS on amd64 and arm64

πŸ“‘ Table of Contents


Quick Start

In a hurry? See QUICKSTART.md β€” minimal copy-paste commands for Linux, macOS, and Windows.

Install latest release

Picks up whatever is currently tagged latest on GitHub β€” and if no release has been published yet, automatically falls back to a source-build from main so you still end up with a working binary.

Windows (PowerShell)

irm https://raw.githubusercontent.com/alimtvnetwork/movie-cli-v7/main/get.ps1 | iex

Linux / macOS

curl -fsSL https://raw.githubusercontent.com/alimtvnetwork/movie-cli-v7/main/get.sh | bash

The bootstrap probes releases/latest/download/install.{ps1,sh} first. If a release exists, it installs the pre-built binary. If not, it transparently falls back to cloning and building from main β€” and prints exactly which path it took. See Installation for flags and details.

Install a specific version (pinned)

Installs exactly the version in the URL β€” never auto-upgrades. Use this for CI pipelines, Dockerfiles, reproducible setups, or when you need to roll back. Replace v2.130.0 with the release tag you want.

Windows (PowerShell)

irm https://github.com/alimtvnetwork/movie-cli-v7/releases/download/v2.130.0/install.ps1 | iex

Linux / macOS

curl -fsSL https://github.com/alimtvnetwork/movie-cli-v7/releases/download/v2.130.0/install.sh | bash

Which one should I use? Use latest for personal machines so you stay current. Use pinned anywhere reproducibility matters β€” the pinned script is hard-locked to the version in the URL and will install that exact tag forever, even after newer releases ship. (contract spec)

Set up & scan
movie config set tmdb_api_key YOUR_KEY
movie scan ~/Downloads
movie ls
Search & discover
movie search "Inception"
movie suggest 5

Every command supports --help or -h for detailed usage.


Sample setup used in this README

Every "Expected output" snippet below assumes the small reference setup shown here. If your library is larger or your IDs differ, only the numbers will change β€” the shape of the output stays the same.

Folder layout (source_folder = /mnt/storage/Movies on Linux/macOS, D:\Media\Movies on Windows):

/mnt/storage/Movies/
β”œβ”€β”€ Inception (2010).mkv
β”œβ”€β”€ The Matrix (1999).mkv
β”œβ”€β”€ Arrival (2016).mkv
β”œβ”€β”€ Interstellar (2014).mkv
β”œβ”€β”€ The Prestige (2006).mkv
└── _unsorted/
    β”œβ”€β”€ inception.2010.1080p.mkv      ← will be cleaned up by `movie rename`
    └── old.movie.1998.mkv             ← becomes a stale entry after deletion

/mnt/storage/Sorted/                   ← destination for `movie move`
└── Action/

Config values (set once with movie config set <key> <value>):

Key Value
source_folder /mnt/storage/Movies
tmdb_api_key your TMDb v3 key
default_player mpv
log_level info

ID β†’ title map (after the first movie scan, your IDs may differ β€” substitute as needed):

ID Title Year Used in section
1 Inception 2010 Discovery & Organization (tag add 1 favorite)
123 Inception 2010 Scanning & Library, File Management
124 The Matrix 1999 Scanning & Library
125 Arrival 2016 Scanning & Library
131 The Prestige 2006 Discovery & Organization (suggest)
412 Old Movie (1998) β€” Maintenance & Debugging (stale entry)
418 Removed.avi β€” Maintenance & Debugging (stale entry)

History entry IDs (created by past move / rename / scan ops):

History ID Op Target
87 move Inception (2010).mkv β†’ Sorted/Action
86 rename The Matrix (1999).mkv
85 scan /mnt/storage/Movies (12 added)
42 generic placeholder used in --id 42 examples β€”

Tip: run movie ls after your first scan to see your real media IDs, and movie undo --list to see your real history IDs. Replace the sample numbers above with yours when copying commands.


βœ… Pre-flight checklist

Run these checks before any command in the Jump to a command section. Each row tells you what to verify, the one-liner that confirms it, and the fix if the check fails. Tick boxes as you go.

βœ“ What to verify Why it matters
☐ movie binary is on $PATH every command starts with movie …
☐ tmdb_api_key is set in config scan / search / suggest fail without it
☐ source_folder is set in config movie scan (no args) needs it
☐ default_player is set in config movie play <id> needs it
☐ source_folder exists and contains video files otherwise scan returns 0 added
☐ Destination folder for movie move is writable otherwise move fails with permission denied
☐ Port 7777 is free (or pick another with --port) needed for movie rest
☐ Network access to api.themoviedb.org needed for TMDb metadata
☐ Network access to GitHub releases needed for movie update
πŸš€ Quick bootstrap check

After installing, run two commands β€” the first confirms the binary is on $PATH, the second runs every check above automatically:

command -v movie >/dev/null && echo "βœ… movie found: $(command -v movie)" || echo "❌ movie NOT on PATH"
movie doctor

On Windows PowerShell, the equivalent first line is:

if (Get-Command movie -ErrorAction SilentlyContinue) { "βœ… movie found: $((Get-Command movie).Source)" } else { "❌ movie NOT on PATH" }
movie doctor

movie doctor runs the config-key, source-folder, port 7777, PATH, deploy and version checks built into the binary, prints the same βœ… / ⚠️ / ❌ output, and exits non-zero if anything fails β€” so it's safe to use in CI. Pass --fix to auto-repair fixable findings, or --json for machine-readable output.

All βœ…? You're ready to run anything in the Jump to a command section. Any ❌? Fix it first β€” most failures further down the README trace back to one of these checks.

🧾 Diffing readme.txt against the Expected output blocks. All six "βœ… Expected output" snippets in the Jump to a command section are plain ```text fences, so you can pull them out with one awk pass and diff them against your generated readme.txt:
awk '/^\*\*βœ… Expected output\*\*/{flag=1;next} flag && /^```text/{cap=1;next} flag && cap && /^```/{cap=0;flag=0;print "---"; next} cap' README.md > expected.txt && diff -u expected.txt readme.txt | less
On Windows PowerShell use Compare-Object (Get-Content expected.txt) (Get-Content readme.txt). Lines that differ are usually just your real IDs/sizes vs the README's sample IDs (123, 87, 412) β€” see Sample setup for the mapping.


Jump to a command

Skip the demo and jump straight to the command you need. Each link drops you into the matching Command Reference subsection β€” with the animated walkthrough, copy-paste Bash + PowerShell examples, expected output, and the full subcommand table.

Each row has both a Bash and a PowerShell fenced block β€” pick the one for your shell, then triple-click any line (or drag-select the whole block) to copy a real, runnable command. The two blocks differ only where shell syntax matters (paths, env vars, quoting).

πŸ’‘ Want one-click copy? Run movie rest --open to launch the dashboard, then press Ctrl/⌘+K to open the command palette β€” it fuzzy-searches every command in this section and copies the exact movie … string with one click. Single-letter shortcuts (S, F, H, D, M, C) jump to each subsection.

πŸ” Search this README right here. On GitHub, press / to open the file-content search, or use your browser's Ctrl/⌘+F to find any command, flag, or section name on this page. The flat command index below is built so a single keyword (scan, undo, tmdb_api_key) lands on the exact row.

πŸ”Ž Flat alphabetical command index β€” press Ctrl/⌘+F and type any keyword

One line per command. Search lands on the exact row; the section name on the right tells you where to jump.

Click the command to jump straight to its README subsection (with the bash/PowerShell blocks, args, expected output, and "if it differs" notes). Each row also carries a stable anchor (e.g. #movie-scan, #movie-undo-list) shown in the rightmost column β€” share that fragment and links always land on the exact row. The Example keyword column gives you a minimal command-shaped placeholder (e.g. movie ls --year , movie scan --dry-run , movie config set tmdb_api_key ) β€” paste it into Ctrl/⌘+F to find every place that argument pattern appears in this README (index row, usage block, expected output, and tips).

πŸ”„ Auto-generated from a single source. scripts/gen-command-index.py owns the canonical command list and rewrites three things in this README on every run: (1) the HTML table + plain-text block in this section, (2) every stale link to one of the six command-section anchors (#scanning--library, #file-management, #history--undo, #discovery--organization, #maintenance--debugging, #configuration--system), and (3) the bash + powershell quick-start pair under each #### πŸ“‚ [Section] heading in the Command Reference (between <!-- SECTION-CMDS:<label>:BEGIN --> markers). Edit only the script, then run python3 scripts/gen-command-index.py. CI runs --check on every push and fails with a file:line annotation if anything is stale. Hand-written prose around the markers (Args / Assumptions / Expected output / If it differs) is preserved untouched.

Command β†’ Section Example keyword Anchor
movie cd <id>β†’File Managementmovie cd #movie-cd-id
movie changelog→Configuration & Systemmovie changelog#movie-changelog
movie cleanup→Maintenance & Debuggingmovie cleanup#movie-cleanup
movie config→Configuration & Systemmovie config#movie-config
movie config get <key>β†’Configuration & Systemmovie config get #movie-config-get-key
movie config set <key> <value>β†’Configuration & Systemmovie config set #movie-config-set-key-value
movie config set source_folder <path>β†’Configuration & Systemmovie config set source_folder #movie-config-set-source-folder-path
movie config set tmdb_api_key <key>β†’Configuration & Systemmovie config set tmdb_api_key #movie-config-set-tmdb-api-key-key
movie db→Maintenance & Debuggingmovie db#movie-db
movie discover→Discovery & Organizationmovie discover#movie-discover
movie duplicates→File Managementmovie duplicates#movie-duplicates
movie export→Maintenance & Debuggingmovie export#movie-export
movie export --format csv --out <file>β†’Maintenance & Debuggingmovie export --format csv #movie-export-format-csv-out-file
movie export --format json --out <file>β†’Maintenance & Debuggingmovie export --format json #movie-export-format-json-out-file
movie hello→Configuration & Systemmovie hello#movie-hello
movie info <id>β†’Scanning & Librarymovie info #movie-info-id
movie info <id> --json→Scanning & Librarymovie info --json #movie-info-id-json
movie logs→Maintenance & Debuggingmovie logs#movie-logs
movie ls→Scanning & Librarymovie ls#movie-ls
movie ls --genre <name>β†’Scanning & Librarymovie ls --genre #movie-ls-genre-name
movie ls --limit <n>β†’Scanning & Librarymovie ls --limit #movie-ls-limit-n
movie ls --year <yyyy> --sort <field>β†’Scanning & Librarymovie ls --year #movie-ls-year-yyyy-sort-field
movie move→File Managementmovie move#movie-move
movie move --all→File Managementmovie move --all#movie-move-all
movie move <id> --to <path>β†’File Managementmovie move --to #movie-move-id-to-path
movie play <id>β†’File Managementmovie play #movie-play-id
movie play <id> --player <bin>β†’File Managementmovie play --player #movie-play-id-player-bin
movie popout→File Managementmovie popout#movie-popout
movie redo→History & Undomovie redo#movie-redo
movie rename→File Managementmovie rename#movie-rename
movie rename <id>β†’File Managementmovie rename #movie-rename-id
movie rename --all --pattern <fmt>β†’File Managementmovie rename --all --pattern #movie-rename-all-pattern-fmt
movie rescan→Scanning & Librarymovie rescan#movie-rescan
movie rest→Maintenance & Debuggingmovie rest#movie-rest
movie rest --open→Maintenance & Debuggingmovie rest --open#movie-rest-open
movie rest --port <n>β†’Maintenance & Debuggingmovie rest --port #movie-rest-port-n
movie scan→Scanning & Librarymovie scan#movie-scan
movie scan <path>β†’Scanning & Librarymovie scan #movie-scan-path
movie scan <path> --dry-run→Scanning & Librarymovie scan --dry-run #movie-scan-path-dry-run
movie scan <path> --refresh→Scanning & Librarymovie scan --refresh #movie-scan-path-refresh
movie search <query>β†’Scanning & Librarymovie search #movie-search-query
movie search <query> --year <yyyy>β†’Scanning & Librarymovie search --year #movie-search-query-year-yyyy
movie stats→Discovery & Organizationmovie stats#movie-stats
movie stats --by <dimension>β†’Discovery & Organizationmovie stats --by #movie-stats-by-dimension
movie suggest→Discovery & Organizationmovie suggest#movie-suggest
movie suggest --genre <name> --limit <n>β†’Discovery & Organizationmovie suggest --genre #movie-suggest-genre-name-limit-n
movie tag add <id> <tag>β†’Discovery & Organizationmovie tag add #movie-tag-add-id-tag
movie tag list <id>β†’Discovery & Organizationmovie tag list #movie-tag-list-id
movie tag list --all→Discovery & Organizationmovie tag list --all#movie-tag-list-all
movie tag remove <id> <tag>β†’Discovery & Organizationmovie tag remove #movie-tag-remove-id-tag
movie tag remove <id> --all→Discovery & Organizationmovie tag remove --all#movie-tag-remove-id-all
movie undo→History & Undomovie undo#movie-undo
movie undo --id <history-id>β†’History & Undomovie undo --id #movie-undo-id-history-id
movie undo --list→History & Undomovie undo --list#movie-undo-list
movie update→Configuration & Systemmovie update#movie-update
movie version→Configuration & Systemmovie version#movie-version
movie watch add <id>β†’Discovery & Organizationmovie watch add #movie-watch-add-id
movie watch add <id> --priority <level>β†’Discovery & Organizationmovie watch add --priority #movie-watch-add-id-priority-level
movie watch list→Discovery & Organizationmovie watch list#movie-watch-list
movie watch list --sort <field>β†’Discovery & Organizationmovie watch list --sort #movie-watch-list-sort-field
πŸ“‹ Plain-text / terminal version β€” same index, fixed-width so the β†’ arrows line up in monospace

Use this block when reading the README in a terminal (cat README.md, less, bat), in a non-HTML editor, or when piping to grep / fzf. Every row is padded so the β†’ pointer sits in a single column and the Section and Anchor columns start at the same offset on every line. Copy the whole block β€” it's ASCII-safe (only the β†’ arrow is non-ASCII, U+2192) and renders cleanly in any UTF-8 monospace font.

Command                                      Section                      Anchor
----------------------------------------   -   ------------------------   ------------------------------------
movie cd <id>                              β†’   File Management            #movie-cd-id
movie changelog                            β†’   Configuration & System     #movie-changelog
movie cleanup                              β†’   Maintenance & Debugging    #movie-cleanup
movie config                               β†’   Configuration & System     #movie-config
movie config get <key>                     β†’   Configuration & System     #movie-config-get-key
movie config set <key> <value>             β†’   Configuration & System     #movie-config-set-key-value
movie config set source_folder <path>      β†’   Configuration & System     #movie-config-set-source-folder-path
movie config set tmdb_api_key <key>        β†’   Configuration & System     #movie-config-set-tmdb-api-key-key
movie db                                   β†’   Maintenance & Debugging    #movie-db
movie discover                             β†’   Discovery & Organization   #movie-discover
movie duplicates                           β†’   File Management            #movie-duplicates
movie export                               β†’   Maintenance & Debugging    #movie-export
movie export --format csv --out <file>     β†’   Maintenance & Debugging    #movie-export-format-csv-out-file
movie export --format json --out <file>    β†’   Maintenance & Debugging    #movie-export-format-json-out-file
movie hello                                β†’   Configuration & System     #movie-hello
movie info <id>                            β†’   Scanning & Library         #movie-info-id
movie info <id> --json                     β†’   Scanning & Library         #movie-info-id-json
movie logs                                 β†’   Maintenance & Debugging    #movie-logs
movie ls                                   β†’   Scanning & Library         #movie-ls
movie ls --genre <name>                    β†’   Scanning & Library         #movie-ls-genre-name
movie ls --limit <n>                       β†’   Scanning & Library         #movie-ls-limit-n
movie ls --year <yyyy> --sort <field>      β†’   Scanning & Library         #movie-ls-year-yyyy-sort-field
movie move                                 β†’   File Management            #movie-move
movie move --all                           β†’   File Management            #movie-move-all
movie move <id> --to <path>                β†’   File Management            #movie-move-id-to-path
movie play <id>                            β†’   File Management            #movie-play-id
movie play <id> --player <bin>             β†’   File Management            #movie-play-id-player-bin
movie popout                               β†’   File Management            #movie-popout
movie redo                                 β†’   History & Undo             #movie-redo
movie rename                               β†’   File Management            #movie-rename
movie rename <id>                          β†’   File Management            #movie-rename-id
movie rename --all --pattern <fmt>         β†’   File Management            #movie-rename-all-pattern-fmt
movie rescan                               β†’   Scanning & Library         #movie-rescan
movie rest                                 β†’   Maintenance & Debugging    #movie-rest
movie rest --open                          β†’   Maintenance & Debugging    #movie-rest-open
movie rest --port <n>                      β†’   Maintenance & Debugging    #movie-rest-port-n
movie scan                                 β†’   Scanning & Library         #movie-scan
movie scan <path>                          β†’   Scanning & Library         #movie-scan-path
movie scan <path> --dry-run                β†’   Scanning & Library         #movie-scan-path-dry-run
movie scan <path> --refresh                β†’   Scanning & Library         #movie-scan-path-refresh
movie search <query>                       β†’   Scanning & Library         #movie-search-query
movie search <query> --year <yyyy>         β†’   Scanning & Library         #movie-search-query-year-yyyy
movie stats                                β†’   Discovery & Organization   #movie-stats
movie stats --by <dimension>               β†’   Discovery & Organization   #movie-stats-by-dimension
movie suggest                              β†’   Discovery & Organization   #movie-suggest
movie suggest --genre <name> --limit <n>   β†’   Discovery & Organization   #movie-suggest-genre-name-limit-n
movie tag add <id> <tag>                   β†’   Discovery & Organization   #movie-tag-add-id-tag
movie tag list <id>                        β†’   Discovery & Organization   #movie-tag-list-id
movie tag list --all                       β†’   Discovery & Organization   #movie-tag-list-all
movie tag remove <id> <tag>                β†’   Discovery & Organization   #movie-tag-remove-id-tag
movie tag remove <id> --all                β†’   Discovery & Organization   #movie-tag-remove-id-all
movie undo                                 β†’   History & Undo             #movie-undo
movie undo --id <history-id>               β†’   History & Undo             #movie-undo-id-history-id
movie undo --list                          β†’   History & Undo             #movie-undo-list
movie update                               β†’   Configuration & System     #movie-update
movie version                              β†’   Configuration & System     #movie-version
movie watch add <id>                       β†’   Discovery & Organization   #movie-watch-add-id
movie watch add <id> --priority <level>    β†’   Discovery & Organization   #movie-watch-add-id-priority-level
movie watch list                           β†’   Discovery & Organization   #movie-watch-list
movie watch list --sort <field>            β†’   Discovery & Organization   #movie-watch-list-sort-field
πŸ“‚ Scanning & Library

Match files against TMDb, browse the library.

movie info <id>
movie info <id> --json
movie ls
movie ls --genre <name>
movie ls --limit <n>
movie ls --year <yyyy> --sort <field>
movie rescan
movie scan
movie scan <path>
movie scan <path> --dry-run
movie scan <path> --refresh
movie search <query>
movie search <query> --year <yyyy>
movie info <id>
movie info <id> --json
movie ls
movie ls --genre <name>
movie ls --limit <n>
movie ls --year <yyyy> --sort <field>
movie rescan
movie scan
movie scan <path>
movie scan <path> --dry-run
movie scan <path> --refresh
movie search <query>
movie search <query> --year <yyyy>

Args: <path> is the folder to scan (defaults to your configured source_folder). 123 is a media ID β€” get one from movie ls. "inception" is any free-text query; quote it if it contains spaces.

Assumptions: source_folder is set (movie config set source_folder /mnt/storage/Movies), tmdb_api_key is set, and that folder contains video files. The sample IDs 123/124/125 come from your own library after the first movie scan.

βœ… Expected output

$ movie scan
β†’ scanning /mnt/storage/Movies
   matched   42
   added     12
   skipped    3
   tmdb hit  41 / miss 1
done in 4.2s

$ movie ls
ID   TITLE                              YEAR   GENRE     RATING   SIZE
123  Inception                          2010   Action     8.8     2.1 GB
124  The Matrix                         1999   Action     8.7     1.8 GB
125  Arrival                            2016   Sci-Fi     7.9     1.4 GB
...  (use --limit / --page to paginate)

$ movie info 123
Inception (2010)         ID 123    β˜… 8.8    Runtime 148m
Genre:    Action, Sci-Fi
Director: Christopher Nolan
File:     /mnt/storage/Movies/Inception (2010).mkv
TMDb:     https://www.themoviedb.org/movie/27205

If it differs: the most common mismatch is 0 added or tmdb miss rates near 100% β€” that means tmdb_api_key is unset or invalid. Fix with movie config set tmdb_api_key <your key> (get one at https://www.themoviedb.org/settings/api), then re-run movie scan. If movie ls is empty after a successful scan, your source_folder points at the wrong directory β€” verify with movie config get source_folder.

πŸ“¦ File Management

Move, rename, flatten, play files.

movie cd <id>
movie duplicates
movie move
movie move --all
movie move <id> --to <path>
movie play <id>
movie play <id> --player <bin>
movie popout
movie rename
movie rename <id>
movie rename --all --pattern <fmt>
movie cd <id>
movie duplicates
movie move
movie move --all
movie move <id> --to <path>
movie play <id>
movie play <id> --player <bin>
movie popout
movie rename
movie rename <id>
movie rename --all --pattern <fmt>

Args: 123 is a media ID (movie ls to find it). --to <path> is the destination folder; quote paths with spaces. move, rename, and popout run interactively when no ID is given.

Assumptions: Media ID 123 exists in your DB (run movie scan first), the destination folder (/mnt/storage/Sorted/Action or D:\Media\Sorted\Action) is writable, and default_player is configured for movie play.

βœ… Expected output

$ movie move 123 --to /mnt/storage/Sorted/Action
β†’ moving "Inception (2010).mkv"
   from  /mnt/storage/Movies
   to    /mnt/storage/Sorted/Action
βœ“ moved  (history id 87)

$ movie rename 123 --dry-run
would rename:
  "inception.2010.1080p.mkv"  β†’  "Inception (2010).mkv"
(dry run β€” no files changed)

$ movie play 123
β†’ launching default player for /mnt/storage/Sorted/Action/Inception (2010).mkv

If it differs: movie move printing error: id 123 not found means your library uses different IDs β€” run movie ls and substitute a real one. permission denied on the destination means the target folder isn't writable: chmod -R u+w /mnt/storage/Sorted (Linux/macOS) or check folder properties on Windows. movie play opening nothing means default_player isn't set β€” fix with movie config set default_player mpv (or vlc, mpv.exe, etc.).

↩️ History & Undo

Reverse any move / rename / scan / delete.

movie redo
movie undo
movie undo --id <history-id>
movie undo --list
movie redo
movie undo
movie undo --id <history-id>
movie undo --list

Args: --id 42 is a history entry ID from movie undo --list. Bare movie undo reverses the most recent operation. movie redo re-applies the last undone op.

Assumptions: At least one prior movie scan, move, or rename has recorded an entry in the history table. The sample IDs 87/86/85 are placeholders β€” substitute the IDs you see in your own movie undo --list.

βœ… Expected output

$ movie undo --list
ID   WHEN                  OP        TARGET
87   2025-04-26 14:02:11   move      Inception (2010).mkv
86   2025-04-26 13:58:40   rename    The Matrix (1999).mkv
85   2025-04-26 13:51:02   scan      /mnt/storage/Movies (12 added)

$ movie undo --id 87
? Revert move of "Inception (2010).mkv"? [y/N] y
βœ“ reverted (history id 87 β†’ reversed)

$ movie redo
βœ“ re-applied move (history id 87)

If it differs: an empty movie undo --list means no reversible operations have been recorded yet β€” run movie scan, movie move, or movie rename first. error: history id 87 not found means 87 is from this README, not your DB; use one from your own movie undo --list. If movie redo fails with nothing to redo, you haven't undone anything in the current session.

🎯 Discovery & Organization

Recommendations, genres, tags, watchlist.

movie discover
movie stats
movie stats --by <dimension>
movie suggest
movie suggest --genre <name> --limit <n>
movie tag add <id> <tag>
movie tag list <id>
movie tag list --all
movie tag remove <id> <tag>
movie tag remove <id> --all
movie watch add <id>
movie watch add <id> --priority <level>
movie watch list
movie watch list --sort <field>
movie discover
movie stats
movie stats --by <dimension>
movie suggest
movie suggest --genre <name> --limit <n>
movie tag add <id> <tag>
movie tag list <id>
movie tag list --all
movie tag remove <id> <tag>
movie tag remove <id> --all
movie watch add <id>
movie watch add <id> --priority <level>
movie watch list
movie watch list --sort <field>

Args: 1 is a media ID (movie ls). favorite is any tag name you choose β€” letters, digits, dashes. movie watch list and movie stats take no args.

Assumptions: Library is non-empty (movie scan has run), tmdb_api_key is set for movie suggest / movie discover, and media ID 1 exists. Stats numbers reflect your own library, not the sample.

βœ… Expected output

$ movie suggest
Because you watched Inception (2010):
  β€’ Interstellar (2014)        β˜… 8.6    not in library
  β€’ Tenet (2020)               β˜… 7.4    not in library
  β€’ The Prestige (2006)        β˜… 8.5    in library (id 131)

$ movie tag add 1 favorite
βœ“ tagged "Inception (2010)" with: favorite

$ movie stats
Library:    248 titles Β· 612 GB
Top genre:  Action (74)
Avg rating: 7.4
Watchlist:  12 pending

If it differs: movie suggest returning no recommendations means your library is too small (TMDb needs at least a few scanned titles to pivot from) β€” scan more first. Wildly different stats numbers are normal; they reflect your library, not the sample. movie tag add 1 favorite failing with media not found means ID 1 doesn't exist in your DB β€” pick a real ID from movie ls.

πŸ›  Maintenance & Debugging

Stale-entry cleanup, logs, REST server.

movie cleanup
movie db
movie export
movie export --format csv --out <file>
movie export --format json --out <file>
movie logs
movie rest
movie rest --open
movie rest --port <n>
movie cleanup
movie db
movie export
movie export --format csv --out <file>
movie export --format json --out <file>
movie logs
movie rest
movie rest --open
movie rest --port <n>

Args: All of these run with no required args. movie rest --open opens the dashboard in your browser; add --port 8080 to override the default port. movie export writes to stdout unless you pass --out <file>.

Assumptions: Default port 7777 is free for movie rest, the current working directory is writable for movie export --out, and the DB exists at the configured path. Stale-entry IDs (412/418) only appear if files were removed outside the CLI.

βœ… Expected output

$ movie cleanup --dry-run
stale entries (file no longer exists):
  ID 412   "Old Movie (1998).mkv"
  ID 418   "Removed.avi"
(dry run β€” pass --yes to delete)

$ movie rest --open
β†’ REST server listening on http://127.0.0.1:7777
β†’ opened browser at http://127.0.0.1:7777/dashboard
(press Ctrl+C to stop)

$ movie export --format csv --out library.csv
βœ“ wrote 248 rows to library.csv

If it differs: movie rest failing with address already in use means port 7777 is taken β€” pass --port 8080 (or any free port). movie cleanup --dry-run printing nothing is good β€” it means no stale entries exist. movie export writing zero rows means the library is empty; run movie scan first. If movie logs shows nothing, lower the threshold with movie config set log_level debug.

βš™οΈ Configuration & System

Settings, TMDb key, version, self-update.

movie changelog
movie config
movie config get <key>
movie config set <key> <value>
movie config set source_folder <path>
movie config set tmdb_api_key <key>
movie hello
movie update
movie version
movie changelog
movie config
movie config get <key>
movie config set <key> <value>
movie config set source_folder <path>
movie config set tmdb_api_key <key>
movie hello
movie update
movie version

Args: tmdb_api_key is the config key name (others: source_folder, default_player, log_level). YOUR_KEY is a real TMDb v3 API key β€” get one at https://www.themoviedb.org/settings/api. movie version and movie update take no args.

Assumptions: The config file exists at its default OS-specific path (created automatically on first run), the user has write access to it, and movie update has network access to GitHub releases. Replace YOUR_KEY with a real TMDb v3 key.

βœ… Expected output

$ movie config
source_folder    = /mnt/storage/Movies
tmdb_api_key     = abcd…5678   (set)
default_player   = mpv
log_level        = info

$ movie config set tmdb_api_key abcd1234efgh5678
βœ“ tmdb_api_key updated

$ movie version
movie v2.191.0  (commit a1b2c3d, built 2025-04-26)

$ movie update
β†’ checking github.com/alimtvnetwork/movie-cli-v7 for newer releases…
βœ“ already on the latest version (v2.191.0)

If it differs: movie config showing tmdb_api_key = (unset) is the #1 cause of every other failure in this README β€” set it now. movie update failing with a network error usually means GitHub is unreachable from your network or a corporate proxy is blocking it; download the latest binary from the Releases page instead. A version older than what movie update reports means the upgrade succeeded but your shell is still pointing at the old binary β€” open a fresh terminal.

πŸš‘ Troubleshooting

Common errors and how to fix them β€” tmdb_api_key not set, 429, database is locked, stale entries.

First time here? Run the env-var check at the top of the Command Reference to confirm TMDB_KEY is set before you scan.


πŸŽ₯ Demo

Six short, looping GIFs β€” one per command group. Each one mirrors a real terminal session, so you can see exactly what to expect before you type a single command.

πŸ“‚ Scanning your library

$ movie scan ~/Downloads

πŸ” Scanning: /home/user/Downloads
  Found 12 video files
  [1/12] Scream.2022.1080p.WEBRip.x264-RARBG.mkv
         β†’ Title: Scream (2022)
         β†’ TMDb: β˜… 6.8 | Horror, Mystery, Thriller
         βœ… Saved to database
  ...
  βœ… Done β€” 12 items scanned, 11 new, 1 updated

🎯 Discovery & suggestions

$ movie suggest 5
  πŸ“½οΈ  Recommendations based on your library:
  1   Nope                           2022   β˜… 6.8    Horror, Sci-Fi
  2   X                              2022   β˜… 6.6    Horror, Mystery
  3   Pearl                          2022   β˜… 7.0    Drama, Horror
  ...

πŸ—‚οΈ File management β€” move, rename, organize

movie move and rename demo


↩️ History, undo & redo


πŸ› οΈ Maintenance & debugging

movie cleanup and doctor demo


βš™οΈ Configuration & system

πŸ“Ή Recording your own demos: use VHS (deterministic, scriptable) or asciinema + agg.

vhs assets/screenshots/cmd-scan-library.tape

Installation

Two flavours β€” pick based on whether you want auto-updates or a frozen version.

Latest release (auto-tracks newest)

Windows (PowerShell)

irm https://raw.githubusercontent.com/alimtvnetwork/movie-cli-v7/main/get.ps1 | iex

Linux / macOS (Bash)

curl -fsSL https://raw.githubusercontent.com/alimtvnetwork/movie-cli-v7/main/get.sh | bash

get.{ps1,sh} first checks releases/latest/download/install.{ps1,sh}. If a release is published it installs the pre-built binary; otherwise it falls back to a source-build from main, prints exactly which path it took, and tells the maintainer how to publish a release so future installs skip the build step.

Pinned to a specific release

Windows (PowerShell)

irm https://github.com/alimtvnetwork/movie-cli-v7/releases/download/v2.130.0/install.ps1 | iex

Linux / macOS (Bash)

curl -fsSL https://github.com/alimtvnetwork/movie-cli-v7/releases/download/v2.130.0/install.sh | bash

The script attached to each release has the version baked in (PINNED_VERSION="v2.130.0") and will install exactly that tag β€” it never falls back to "latest" and never delegates to the bootstrap scripts. Replace v2.130.0 with any published release.

When to use which

  • Latest β€” personal machines, demos, "just give me the newest one"
  • Pinned β€” CI pipelines, Dockerfiles, onboarding docs, reproducing a bug on a specific version, controlled rollbacks

Both URLs point at installer assets attached to the GitHub Release. The repo-root install.ps1 and install.sh are unrelated source bootstrap scripts for building locally.

Installer Options

Windows (PowerShell):

Flag Description Example
-InstallDir Custom install directory -InstallDir C:\tools\movie
-Arch Force architecture (amd64, arm64) -Arch arm64
-NoPath Skip adding to user PATH -NoPath

Linux / macOS (Bash):

Flag Description Example
--dir Custom install directory --dir ~/bin
--arch Force architecture (amd64, arm64) --arch arm64
--no-path Skip adding to PATH --no-path
Clone & Build (Development)

Prerequisites:

Requirement Minimum Check
Go 1.22+ go version
Git 2.x git --version
PowerShell 5.1+ (Win) / 7+ (Unix) $PSVersionTable.PSVersion
git clone https://github.com/alimtvnetwork/movie-cli-v7.git
cd movie-cli-v7
pwsh run.ps1

Using the bootstrap installer:

pwsh install.ps1                      # Fresh install (clone + build + deploy)
pwsh install.ps1 -DeployPath ~/bin    # Custom deploy path
Verify
movie version
# v1.0.0 (commit: abc1234, built: 2026-04-09)
#   Go:   go1.22.0
#   OS:   linux/amd64

Tip: If movie is not found, add the deploy path to your PATH. Default: E:\bin-run (Windows) or /usr/local/bin (Unix) for source builds.


What It Does

A portable CLI that manages your personal movie and TV show library entirely from the terminal. Every scan produces:

  • Database β€” structured metadata in SQLite (WAL mode)
  • Thumbnails β€” poster images downloaded from TMDb
  • JSON β€” per-file metadata written to ./data/json/
  • Clean filenames β€” Scream.2022.1080p.WEBRip.x264.mkv β†’ Scream (2022).mkv

All data lives in ./data/ at the project root.


Command Reference

Each section below shows a real-world example of what the command does. Each thumbnail is a short looping walkthrough β€” hover or click to view the full-size still.

πŸ’‘ PowerShell vs Bash quick reference β€” escaping paths & passing env vars in the examples below

The example commands are written in Bash (macOS / Linux / WSL / Git Bash). On Windows PowerShell a few things differ β€” use this table to translate any example before running it:

Concept Bash (macOS / Linux / WSL) PowerShell (Windows)
Home folder ~/Downloads $HOME\Downloads or $env:USERPROFILE\Downloads
Path with spaces "My Movies/Action Films" (double quotes) 'My Movies\Action Films' (single quotes β€” no variable expansion)
Path separator / \ (PowerShell also accepts /)
Escape a literal quote \" inside "..." ` " (backtick + quote) or use '...'
Read an env var $TMDB_KEY $env:TMDB_KEY
Set env var (one command) TMDB_KEY=abc movie scan ~/Downloads $env:TMDB_KEY="abc"; movie scan $HOME\Downloads
Set env var (whole session) export TMDB_KEY=abc $env:TMDB_KEY = "abc"
Set env var (persistent) add export ... to ~/.bashrc / ~/.zshrc [Environment]::SetEnvironmentVariable("TMDB_KEY","abc","User")
Command substitution cd $(movie cd Movies) Set-Location (movie cd Movies)
Line continuation trailing \ trailing ` (backtick)
Comments # comment # comment (same)

Rule of thumb: if an example uses ~, $VAR, \", or $(...), swap it for the PowerShell equivalent above. Everything else (flags, subcommands, IDs) is identical across shells.

πŸ”Ž Check your env vars β€” confirm TMDB_KEY is set before running the examples

Run this once at the start of a session. It prints set / MISSING for each variable the CLI looks at, so you catch a missing TMDb token before a movie scan fails halfway through.

Bash (macOS / Linux / WSL / Git Bash)

for v in TMDB_KEY TMDB_API_KEY MOVIE_CONFIG MOVIE_DB_PATH; do
  if [ -n "${!v}" ]; then
    echo "βœ” $v is set (${#v} chars: ${!v:0:4}…)"
  else
    echo "✘ $v is MISSING"
  fi
done

PowerShell (Windows)

foreach ($v in 'TMDB_KEY','TMDB_API_KEY','MOVIE_CONFIG','MOVIE_DB_PATH') {
  $val = [Environment]::GetEnvironmentVariable($v)
  if ($val) {
    Write-Host "βœ” $v is set ($($val.Length) chars: $($val.Substring(0,[Math]::Min(4,$val.Length)))…)"
  } else {
    Write-Host "✘ $v is MISSING"
  }
}

Expected output when everything is configured:

βœ” TMDB_KEY is set (32 chars: a1b2…)
✘ TMDB_API_KEY is MISSING        ← optional alias, safe to ignore if TMDB_KEY is set
βœ” MOVIE_CONFIG is set (28 chars: /Use…)
✘ MOVIE_DB_PATH is MISSING       ← optional, falls back to the default DB location

Only TMDB_KEY is required for TMDb-backed commands (scan, search, discover, suggest). If it shows MISSING, set it with export TMDB_KEY=... (Bash) or $env:TMDB_KEY = "..." (PowerShell), or persist it via movie config set tmdb_api_key YOUR_KEY.

Scanning & Library


πŸ“Έ movie scan walks a folder, cleans messy release names, and matches each file against TMDb.

β–Ά Try the example from the screenshot β€” replace ~/Downloads with any folder containing video files:

# 1. Reproduce the walkthrough above
movie scan ~/Downloads               # ← swap for your own scan folder

# 2. Re-run for any unmatched titles after the first pass
movie rescan

# 3. Confirm what landed in the library
movie ls

Path placeholders: ~/Downloads = macOS/Linux home folder. On Windows use C:\Users\<you>\Downloads or $env:USERPROFILE\Downloads in PowerShell.

πŸͺŸ PowerShell version (copy-paste on Windows)
# 1. Reproduce the walkthrough above
movie scan "$env:USERPROFILE\Downloads"   # ← swap for your own scan folder

# 2. Re-run for any unmatched titles after the first pass
movie rescan

# 3. Confirm what landed in the library
movie ls
βœ… Expected output (sample β€” yours will list your own files)
Scanning ~/Downloads ... found 12 video files
  βœ” Inception.2010.1080p.mkv          β†’ Inception (2010)            β˜… 8.4
  βœ” The.Batman.2022.WEB.mp4           β†’ The Batman (2022)           β˜… 7.8
  βœ” Dune.Part.Two.2024.2160p.mkv      β†’ Dune: Part Two (2024)       β˜… 8.3
  ⚠ random_clip.mp4                   β†’ no TMDb match (run `movie rescan` later)
Saved 11 entries to library. Run `movie ls` to browse.
Command Description
movie scan [folder] Scan folder β†’ DB + TMDb metadata
movie rescan Re-fetch TMDb metadata for entries with missing data
movie ls Paginated interactive library browser
movie search <name> Live TMDb search β†’ save to DB
movie info <id|title> Detail view (local DB β†’ TMDb fallback)
movie scan ~/Downloads            # scan folder, fetch metadata + posters
movie rescan                      # re-fetch missing genres/ratings from TMDb
movie ls                          # browse library with pagination
movie search "Inception"          # search TMDb and save result
movie info 1                      # show details for media ID 1
movie info "The Batman"           # search by title

File Management

Animated walkthrough of movie move showing planned destinations and a batch confirmation
πŸ“Έ movie move previews the destination for every file before touching the filesystem β€” fully reversible with movie undo.

β–Ά Try the example from the screenshot β€” preview destinations, accept with a, then undo if needed:

# 1. Interactive preview (the walkthrough's "Select [a]ll, [n]one, or numbers" prompt)
movie move ~/Downloads               # ← swap for your own source folder

# 2. Or batch-route everything by type (Movies/ vs TV/)
movie move --all ~/Downloads

# 3. Changed your mind? Reverse the entire batch
movie undo

Path placeholders: ~/Downloads = macOS/Linux. Windows: C:\Users\<you>\Downloads or $env:USERPROFILE\Downloads.

πŸͺŸ PowerShell version (copy-paste on Windows)
# 1. Interactive preview (the walkthrough's "Select [a]ll, [n]one, or numbers" prompt)
movie move "$env:USERPROFILE\Downloads"   # ← swap for your own source folder

# 2. Or batch-route everything by type (Movies\ vs TV\)
movie move --all "$env:USERPROFILE\Downloads"

# 3. Changed your mind? Reverse the entire batch
movie undo
βœ… Expected output (sample preview before confirmation)
Planned moves (3):
  [1] Inception.2010.1080p.mkv      β†’ Movies/Inception (2010)/Inception.2010.1080p.mkv
  [2] The.Batman.2022.WEB.mp4       β†’ Movies/The Batman (2022)/The Batman.2022.mp4
  [3] Breaking.Bad.S01E01.mkv       β†’ TV/Breaking Bad/Season 01/S01E01.mkv
Select [a]ll, [n]one, or numbers (e.g. 1,3): a
βœ” Moved 3 files. Undo with `movie undo` (batch id 87).
Command Description
movie move [directory] Browse, select, move with clean name
movie move --all Batch move all files (auto-route by type)
movie rename Batch rename to clean format
movie popout [directory] Extract video files from subfolders to root
movie play <id> Open with default video player
movie cd [folder-name] Print path of a scanned folder for quick nav
movie move ~/Downloads            # interactive single-file move
movie move --all ~/Downloads      # batch move all files
movie rename                      # clean all filenames
movie popout ~/Downloads          # flatten nested subfolders
movie play 1                      # play with system player
cd $(movie cd Movies)             # navigate to scanned folder

History & Undo

Animated walkthrough of movie undo --list followed by movie undo --id 42 reverting a batch of moves
πŸ“Έ Every move, rename, scan, and delete is tracked. movie undo --list shows what can be reversed; movie redo re-applies it.

β–Ά Try the example from the screenshot β€” list operations, undo a specific batch by ID, then redo it:

# 1. List recent operations (the walkthrough's "ID  When  Action  Target" table)
movie undo --list

# 2. Revert the batch you saw β€” replace 42 with the ID from your own list
movie undo --id 42                   # ← swap 42 for the ID you want to revert

# 3. Re-apply if you undid by mistake
movie redo

ID placeholder: 42 is a sample undo ID. Run movie undo --list to see your own IDs.

πŸͺŸ PowerShell version (copy-paste on Windows)
# 1. List recent operations (the walkthrough's "ID  When  Action  Target" table)
movie undo --list

# 2. Revert the batch you saw β€” replace 42 with the ID from your own list
movie undo --id 42                        # ← swap 42 for the ID you want to revert

# 3. Re-apply if you undid by mistake
movie redo
βœ… Expected output (sample β€” IDs and timestamps will differ)
ID   When              Action   Target
──   ────────────────  ───────  ─────────────────────────────────────────────
42   2025-04-20 14:02  move     3 files β†’ Movies/
41   2025-04-20 13:55  rename   7 files cleaned
40   2025-04-20 12:10  scan     12 entries added

$ movie undo --id 42
βœ” Reverted batch 42 β€” 3 files restored to original locations.
Command Description
movie undo Revert last move/rename/delete/scan operation
movie undo --list Show recent undoable actions
movie undo --batch Undo the entire last batch (e.g. a full scan)
movie undo --id <id> Undo a specific action by ID
movie redo Re-apply the last undone operation
movie history Show history of all tracked operations
movie undo                        # revert most recent operation
movie undo --list                 # see what can be undone
movie undo --batch                # undo entire last scan batch
movie undo --id 42                # undo specific action
movie redo                        # re-apply last undone operation
movie history                     # view full operation history

Discovery & Organization

Animated walkthrough of movie suggest showing personalized recommendations and trending titles
πŸ“Έ movie suggest reads your library tastes and surfaces both personalized picks and trending titles from TMDb.

β–Ά Try the example from the screenshot β€” get 5 picks, browse a genre, then add one to your watchlist:

# 1. Reproduce the walkthrough's 5-item recommendation block
movie suggest 5                      # ← change the number for more/fewer picks

# 2. Drill into a specific genre
movie discover Sci-Fi                # ← swap for Action, Comedy, Horror, etc.

# 3. Bookmark something to watch later (use any ID from `movie ls`)
movie watch add 3                    # ← swap 3 for your chosen media ID

Number / genre / ID placeholders: 5 = pick count; Sci-Fi = any genre; 3 = media ID from your movie ls.

πŸͺŸ PowerShell version (copy-paste on Windows)
# 1. Reproduce the walkthrough's 5-item recommendation block
movie suggest 5                           # ← change the number for more/fewer picks

# 2. Drill into a specific genre (quote names containing a hyphen to be safe)
movie discover "Sci-Fi"                   # ← swap for Action, Comedy, Horror, etc.

# 3. Bookmark something to watch later (use any ID from `movie ls`)
movie watch add 3                         # ← swap 3 for your chosen media ID
βœ… Expected output (sample β€” picks vary based on your library)
Top 5 picks for you (based on your top genres: Sci-Fi, Thriller):
  1. Arrival (2016)              β˜… 7.9   Sci-Fi Β· Drama
  2. Edge of Tomorrow (2014)     β˜… 7.9   Sci-Fi Β· Action
  3. Ex Machina (2014)           β˜… 7.7   Sci-Fi Β· Thriller
  4. Annihilation (2018)         β˜… 6.8   Sci-Fi Β· Horror
  5. Coherence (2013)            β˜… 7.2   Sci-Fi Β· Mystery

βœ” Added "Arrival (2016)" to watchlist (id 3).
Command Description
movie suggest [N] Genre-based recommendations + trending
movie discover [genre] Browse TMDb by genre (interactive picker or direct)
movie tag add <id> <tag> Add a tag to a media item
movie tag remove <id> <tag> Remove a tag
movie tag list [id] List tags (per item or all)
movie watch add <id> Add a library item to your watchlist
movie watch done <id> Mark a title as watched
movie watch undo <id> Revert a title back to to-watch
movie watch rm <id> Remove a title from your watchlist
movie watch ls List your watchlist
movie watch export Export watchlist as JSON for backup
movie watch import <file> Import watchlist from JSON
movie stats Counts, storage, genre chart, avg ratings
movie duplicates Detect duplicate media entries
movie suggest 5                   # get 5 recommendations
movie discover                    # interactive genre picker
movie discover Action             # discover Action movies from TMDb
movie discover Comedy --type tv   # discover Comedy TV shows
movie discover Horror --page 2    # page 2 of Horror movies
movie tag add 1 favorite          # tag media #1 as favorite
movie tag list                    # list all tags
movie watch add 3                 # add media #3 to watchlist
movie watch done 3                # mark as watched
movie watch ls                    # view watchlist
movie stats                       # library statistics
movie duplicates                  # find duplicate entries

Maintenance & Debugging

Animated walkthrough of movie stats showing library counts, total size, and a top-genres bar chart
πŸ“Έ movie stats renders an instant overview β€” counts, storage used, top genres, and average rating.

β–Ά Try the example from the screenshot β€” view stats, then prune any stale entries it surfaces:

# 1. Reproduce the walkthrough's library overview + top-genres chart
movie stats

# 2. Dry-run a cleanup to see entries whose files no longer exist
movie cleanup

# 3. Actually remove them once you're happy with the dry-run output
movie cleanup --remove

No placeholders here β€” movie stats and movie cleanup run as-is.

πŸͺŸ PowerShell version (copy-paste on Windows)
# 1. Reproduce the walkthrough's library overview + top-genres chart
movie stats

# 2. Dry-run a cleanup to see entries whose files no longer exist
movie cleanup

# 3. Actually remove them once you're happy with the dry-run output
movie cleanup --remove
βœ… Expected output (sample β€” numbers reflect your library)
Library: 142 titles Β· 118 movies Β· 24 TV shows Β· 1.7 TB
Top genres:  Drama β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 38   Sci-Fi β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 26   Action β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 19
Average rating: β˜… 7.4

$ movie cleanup
Stale entries (files missing on disk): 4
  - Old.Movie.2009.avi          (id 17)
  - Removed.Show.S02E03.mkv     (id 88)
Run `movie cleanup --remove` to delete these from the database.
Command Description
movie cleanup Find stale entries where files no longer exist
movie cleanup --remove Delete stale entries (not just preview)
movie db Show resolved database path and status
movie logs Display recent error logs from the database
movie rest Start a local REST API server for the library
movie rest --open Start server and open in browser
movie export [-o path] Dump media table as JSON
movie cleanup                     # dry run β€” show stale entries
movie cleanup --remove            # actually remove stale entries
movie db                          # check database location
movie logs                        # view recent error/warning logs
movie rest                        # start REST API on localhost
movie rest --open                 # start and open browser
movie export -o ~/library.json    # export full library as JSON

Configuration & System

Animated walkthrough of movie config showing config keys, setting tmdb_api_key, and movie version output
πŸ“Έ movie config shows every setting; movie version prints the exact build for bug reports.

β–Ά Try the example from the screenshot β€” inspect config, set the TMDb key, then verify the build:

# 1. Reproduce the walkthrough's "Current configuration" block
movie config

# 2. Set your own TMDb API key (replace YOUR_KEY with the real value)
movie config set tmdb_api_key YOUR_KEY        # ← swap YOUR_KEY for your TMDb token

# 3. Confirm exactly which build is running (use this in bug reports)
movie version

Key placeholder: YOUR_KEY = your TMDb API token from https://www.themoviedb.org/settings/api.

πŸͺŸ PowerShell version (copy-paste on Windows)
# 1. Reproduce the walkthrough's "Current configuration" block
movie config

# 2. Set your own TMDb API key (replace YOUR_KEY with the real value)
#    Tip: store it in an env var first so it doesn't end up in shell history:
#       $env:TMDB_KEY = "your-real-token"
movie config set tmdb_api_key $env:TMDB_KEY   # ← or pass the literal token in quotes

# 3. Confirm exactly which build is running (use this in bug reports)
movie version
βœ… Expected output (sample β€” your build info will differ)
Current configuration:
  tmdb_api_key   ********************abcd   (set)
  library_root   ~/Media
  player         vlc
  log_level      info

$ movie config set tmdb_api_key YOUR_KEY
βœ” Saved tmdb_api_key.

$ movie version
movie v2.178.0   commit a1b2c3d   built 2025-04-26   go1.22.2 darwin/arm64
Command Description
movie config Show all configuration
movie config set <key> <val> Set a config value
movie version Version, commit, build date, Go, OS/arch
movie update Pull latest, rebuild, and deploy (copy-and-handoff)
movie update-cleanup Remove leftover temp binaries and .bak backups
movie changelog [--latest] Show release notes
movie config set movies_dir ~/Movies
movie config set tmdb_api_key YOUR_KEY
movie update                          # full self-update: pull β†’ build β†’ deploy
movie update-cleanup                  # remove temp update artifacts
movie changelog --latest

Config keys:

Key Default Purpose
movies_dir ~/Movies Movie file destination
tv_dir ~/TVShows TV show destination
archive_dir ~/Archive Archive destination
scan_dir ~/Downloads Default scan source
tmdb_api_key (none) TMDb API key
page_size 20 Items per page in ls

Troubleshooting

Quick Diagnosis Flowchart

Not sure which error you're seeing? Follow this decision tree to find the right fix in seconds.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  What happened when you ran the     β”‚
β”‚  command?                           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚                     β”‚
β”Œβ”€β”€β”€β–Όβ”€β”€β”€β”€β”           β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”
β”‚ Every  β”‚           β”‚ Some or β”‚
β”‚ file   β”‚           β”‚ all got β”‚
β”‚ shows  β”‚           β”‚ skipped β”‚
β”‚ "no    β”‚           β”‚ with an β”‚
β”‚ TMDb   β”‚           β”‚ error   β”‚
β”‚ match" β”‚           β”‚ code    β”‚
β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜           β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
    β”‚                     β”‚
    β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚         β”‚           β”‚           β”‚
    β”‚    β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”
    β”‚    β”‚ 429 /   β”‚ β”‚ 401 /   β”‚ β”‚ timeout β”‚
    β”‚    β”‚ "too     β”‚ β”‚ "unauth-β”‚ β”‚ / DNS   β”‚
    β”‚    β”‚ many    β”‚ β”‚ orized" β”‚ β”‚ failure β”‚
    β”‚    β”‚ requests"β”‚ β”‚         β”‚ β”‚         β”‚
    β”‚    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
    β”‚         β”‚           β”‚           β”‚
    β”‚    β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”
    β”‚    β”‚ Wait &  β”‚ β”‚ Check   β”‚ β”‚ Check   β”‚
    β”‚    β”‚ re-run  β”‚ β”‚ your    β”‚ β”‚ network β”‚
    β”‚    β”‚ rescan  β”‚ β”‚ API key β”‚ β”‚ / proxy β”‚
    β”‚    β”‚         β”‚ β”‚         β”‚ β”‚ settingsβ”‚
    β”‚    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
    β”‚         β”‚           β”‚           β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  "database is      β”‚
    β”‚  locked" or        β”‚
    β”‚  SQLITE_BUSY       β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚  Kill any other    β”‚
    β”‚  movie process,    β”‚
    β”‚  then retry        β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Map the symptom to the fix:

Symptom Likely cause Jump to fix
Every file shows no TMDb match API key missing or wrong 1. tmdb_api_key not set
429 too many requests Rate limit hit during large scan 5. TMDb 429 Too Many Requests
database is locked / SQLITE_BUSY Second movie process running 8. database is locked
1. tmdb_api_key not set β€” TMDb requests are skipped

Symptom: movie scan runs but every file is reported as ! no TMDb match β€” saved as Unknown (see the warning row in the scan walkthrough).

Cause: No TMDb API key configured. The scanner falls back to filename-only parsing.

Fix:

movie config set tmdb_api_key YOUR_KEY      # see assets/screenshots/cmd-config-system.gif
movie config                                 # confirm: tmdb_api_key = ********  (set)
movie rescan                                 # backfill metadata for previously-unmatched entries

If the key is set but matches still fail, see error #5 (rate limits).


2. no TMDb match for a known title

Symptom: A file you recognize ends up unmatched in the scan walkthrough, tagged ⚠ no TMDb match.

Cause: The release filename is too noisy for the cleaner (extra release-group tags, unusual separators, foreign titles).

Fix: Search and link manually.

movie search "The Matrix"           # live TMDb search
movie info "The Matrix"             # confirm the right title
movie rescan                        # re-resolve everything still missing metadata

If the title genuinely isn't in TMDb, the OMDb fallback kicks in automatically when OMDB_API_KEY is set (see error #6).


3. move refuses to run β€” destination directory missing

Symptom: movie move aborts before showing the planned destinations from the file-management walkthrough, printing movies_dir does not exist or tv_dir does not exist.

Cause: movies_dir / tv_dir point to a folder that hasn't been created yet.

Fix:

movie config                                 # check current paths
mkdir -p ~/Movies ~/TVShows                  # create the destinations
movie config set movies_dir ~/Movies         # or repoint to an existing folder
movie config set tv_dir ~/TVShows

4. Wrong files moved β€” need to roll back

Symptom: A movie move --all or movie rename batch put files in unexpected places.

Fix: Every operation is tracked. Use the flow shown in the history & undo walkthrough:

movie undo --list                # find the batch ID (e.g. 42)
movie undo --id 42               # revert exactly that batch
# changed your mind?
movie redo                       # re-apply the last undone operation

movie undo always works in reverse chronological order β€” there is no "permanent" move.


5. TMDb 429 Too Many Requests β€” rate limited

Symptom: movie scan or movie suggest (see the discovery walkthrough) prints tmdb: 429 too many requests and skips entries.

Cause: TMDb caps free keys at ~50 requests / second. Large scans can briefly exceed it.

Fix: The scanner backs off automatically; just re-run the resolver after a short pause:

sleep 5 && movie rescan          # backfill anything skipped
movie logs                       # inspect any retained warnings

6. OMDB_API_KEY not set β€” fallback tier silently disabled

Symptom: Some titles still show as Unknown even after movie rescan, and movie logs shows omdb: tier skipped (no key).

Cause: OMDb is the secondary provider used when TMDb has no result. It's opt-in and reads only from the environment β€” never the config file or repo.

Fix:

export OMDB_API_KEY=your_omdb_key            # add to your shell profile to persist
movie rescan
movie logs                                   # confirm the omdb-skip warnings are gone

If you also see omdb: 401 unauthorized, the key is wrong β€” generate a new one at omdbapi.com.


7. Stale entries β€” files were moved/deleted outside the CLI

Symptom: movie ls shows entries whose files no longer exist on disk. movie stats (see the maintenance walkthrough) over-reports Total size.

Fix:

movie cleanup                    # dry-run: list stale entries
movie cleanup --remove           # actually delete them from the DB
movie duplicates                 # also surface accidental dupes after a cleanup

8. database is locked β€” second movie process running

Symptom: Any command exits with database is locked or SQLITE_BUSY.

Cause: SQLite WAL allows many readers but only one writer at a time. A long-running movie rest server or a hung movie scan can hold the write lock.

Fix:

movie db                         # confirms the path of the locked DB
# stop any running 'movie rest' / 'movie scan'
ps -ef | grep -i movie           # find lingering processes
kill <pid>

If the lock persists after killing all processes, delete data/movie.db-wal and data/movie.db-shm (the live DB file is safe to keep).


9. command not found: movie after movie update

Symptom: Self-update appears to succeed but the next invocation prints command not found.

Cause: The new binary was deployed to a directory not on $PATH, or shell hash cache is stale.

Fix:

movie update-cleanup             # remove any half-installed temp binaries
hash -r                          # bash/zsh: clear the command cache
which movie                      # verify the resolved path
movie version                    # confirm the new build (see assets/screenshots/cmd-config-system.gif)

On Windows, restart the terminal so the updated PATH is picked up.


Still stuck?
  1. Run movie version and include the output in any bug report β€” it pins down the exact commit and build date.
  2. Run movie logs β€” the most recent error rows usually point straight at the failing layer (TMDb / DB / filesystem).
  3. Open an issue with the version line, the failing command, and the relevant logs excerpt.

Command Tree

movie
β”œβ”€β”€ hello                         # Greeting with version
β”œβ”€β”€ version                       # Version, commit, build date, Go, OS/arch
β”œβ”€β”€ changelog [--latest]          # Show changelog (full or latest version)
β”œβ”€β”€ update                        # Pull β†’ rebuild β†’ deploy (copy-and-handoff)
β”œβ”€β”€ update-cleanup                # Remove temp update artifacts
β”œβ”€β”€ config [get|set] [key]        # View/set configuration
β”œβ”€β”€ scan [folder]                 # Scan folder β†’ DB + TMDb metadata
β”œβ”€β”€ rescan                        # Re-fetch missing TMDb metadata
β”œβ”€β”€ ls                            # Paginated library list (file-backed only)
β”œβ”€β”€ search <name>                 # Live TMDb search β†’ save to DB
β”œβ”€β”€ info <id|title>               # Detail view (local DB β†’ TMDb fallback)
β”œβ”€β”€ suggest [N]                   # Recommendations + trending
β”œβ”€β”€ discover [genre]              # Browse TMDb by genre
β”œβ”€β”€ move [directory]              # Browse, select, move with clean name
β”œβ”€β”€ rename                        # Batch rename to clean format
β”œβ”€β”€ popout [directory]            # Extract videos from subfolders
β”œβ”€β”€ undo [--list|--batch|--id]    # Revert operations (move/delete/scan)
β”œβ”€β”€ redo                          # Re-apply last undone operation
β”œβ”€β”€ history                       # Show all tracked operations
β”œβ”€β”€ play <id>                     # Open with default video player
β”œβ”€β”€ stats                         # Counts, storage, genre chart, avg ratings
β”œβ”€β”€ duplicates                    # Detect duplicate media entries
β”œβ”€β”€ cleanup [--remove]            # Find/remove stale entries
β”œβ”€β”€ tag [add|remove|list]         # Manage user-defined tags
β”œβ”€β”€ watch [add|done|undo|rm|ls|export|import]  # Manage watchlist + sync
β”œβ”€β”€ cd [folder-name]              # Print scanned folder path
β”œβ”€β”€ export [-o path]              # Dump media table as JSON
β”œβ”€β”€ db                            # Show database path and status
β”œβ”€β”€ logs                          # View error/warning logs
└── rest [--open]                 # Start local REST API server

Build & Deploy

Makefile Targets
Target Description
make build Compile for current platform
make build-all Cross-compile all 6 targets into dist/
make build-windows Windows amd64 (with embedded icon)
make build-windows-arm Windows arm64 (with embedded icon)
make build-mac-arm macOS ARM64
make build-mac-intel macOS amd64
make build-linux Linux amd64
make build-linux-arm Linux arm64
make install Build + copy to /usr/local/bin
Build via run.ps1
.\run.ps1                           # Full pipeline: pull, build, deploy
.\run.ps1 -NoPull                   # Skip git pull
.\run.ps1 -NoPull -NoDeploy        # Build only
.\run.ps1 -R movie scan D:\movies  # Build + run scan
.\run.ps1 -t                       # Run all unit tests
.\run.ps1 -ForcePull               # CI mode: discard changes + pull
Flag Description
-NoPull Skip git pull
-NoDeploy Skip deploy step
-R Run movie after build (trailing args forwarded)
-t Run all unit tests
-ForcePull CI mode: discard changes + pull

See spec/03-general/04-run-guide.md for the full usage guide.


Release Workflow

Releases are fully automated via GitHub Actions. Pushing to a release/** branch or a v* tag triggers:

  1. Cross-compilation β€” 6 binaries (Windows/Linux/macOS Γ— amd64/arm64)
  2. Packaging β€” .zip (Windows) and .tar.gz (Unix)
  3. SHA256 checksums β€” checksums.txt with all artifact hashes
  4. Install scripts β€” version-pinned install.ps1 and install.sh
  5. GitHub Release β€” formatted page with changelog, checksums, and install instructions
Creating a Release
# Option A: Push a release branch
git checkout -b release/v1.3.0
git push origin release/v1.3.0

# Option B: Tag directly
git tag v1.3.0
git push origin v1.3.0

Both trigger the same pipeline. Version is resolved from the ref name.

CI Pipeline: Pushing a release/* branch or v* tag triggers GitHub Actions to cross-compile 6 targets, generate checksums, and create a GitHub release with changelog and install instructions.

See spec/12-ci-cd-pipeline/02-release-pipeline.md for the full pipeline spec.


Project Structure

movie-cli-v7/
β”œβ”€β”€ main.go                        # Entry point
β”œβ”€β”€ cmd/                           # Cobra commands (one file per command)
β”‚   β”œβ”€β”€ root.go                    # Root command, registers subcommands
β”‚   β”œβ”€β”€ movie_config.go            # config get/set
β”‚   β”œβ”€β”€ movie_scan.go              # scan folder
β”‚   β”œβ”€β”€ movie_rescan.go            # re-fetch missing metadata
β”‚   β”œβ”€β”€ movie_ls.go                # paginated list
β”‚   β”œβ”€β”€ movie_search.go            # TMDb search
β”‚   β”œβ”€β”€ movie_info.go              # detail view + shared fetch helpers
β”‚   β”œβ”€β”€ movie_suggest.go           # recommendations
β”‚   β”œβ”€β”€ movie_move.go              # interactive move
β”‚   β”œβ”€β”€ movie_rename.go            # batch rename
β”‚   β”œβ”€β”€ movie_popout.go            # extract from subfolders
β”‚   β”œβ”€β”€ movie_undo.go              # undo operations
β”‚   β”œβ”€β”€ movie_redo.go              # redo undone operations
β”‚   β”œβ”€β”€ movie_history.go           # operation history
β”‚   β”œβ”€β”€ movie_play.go              # play with system player
β”‚   β”œβ”€β”€ movie_stats.go             # library statistics
β”‚   β”œβ”€β”€ movie_duplicates.go        # duplicate detection
β”‚   β”œβ”€β”€ movie_cleanup.go           # stale entry cleanup
β”‚   β”œβ”€β”€ movie_tag.go               # tag management
β”‚   β”œβ”€β”€ movie_watch.go             # watchlist management
β”‚   β”œβ”€β”€ movie_cd.go                # folder navigation helper
β”‚   β”œβ”€β”€ movie_export.go            # JSON export
β”‚   β”œβ”€β”€ movie_db.go                # database path/status
β”‚   β”œβ”€β”€ movie_logs.go              # error log viewer
β”‚   β”œβ”€β”€ movie_rest.go              # REST API server
β”‚   └── movie_resolve.go           # shared ID/title resolver
β”œβ”€β”€ cleaner/cleaner.go             # Filename cleaning + slug generation
β”œβ”€β”€ tmdb/client.go                 # TMDb API client
β”œβ”€β”€ db/                            # SQLite database layer
β”‚   β”œβ”€β”€ db.go                      # Connection + migrations
β”‚   β”œβ”€β”€ media.go                   # Media CRUD operations
β”‚   β”œβ”€β”€ config.go                  # Config get/set
β”‚   └── history.go                 # Move + scan history
β”œβ”€β”€ errlog/                        # Centralized error/warning logging
β”‚   └── errlog.go                  # File + DB logging with stack traces
β”œβ”€β”€ updater/                       # Copy-and-handoff self-update
β”‚   β”œβ”€β”€ run.go                     # Entry points: Run() + RunWorker()
β”‚   β”œβ”€β”€ repo.go                    # Repo path resolution
β”‚   β”œβ”€β”€ handoff.go                 # Binary copy + foreground launch
β”‚   β”œβ”€β”€ script.go                  # PowerShell script generation
β”‚   └── cleanup.go                 # Temp artifact removal
β”œβ”€β”€ version/version.go             # Build-time version variables
β”œβ”€β”€ .github/
β”‚   └── workflows/
β”‚       β”œβ”€β”€ ci.yml                 # Lint + test + vulncheck + cross-build
β”‚       β”œβ”€β”€ release.yml            # Cross-compile + GitHub Release
β”‚       └── vulncheck.yml          # Weekly vulnerability scan
β”œβ”€β”€ run.ps1                        # PowerShell build + deploy pipeline
β”œβ”€β”€ install.ps1                    # Bootstrap installer
β”œβ”€β”€ CHANGELOG.md                   # Release notes
└── spec/                          # Detailed specifications

Data Storage

All data lives in ./data/:

./data/
β”œβ”€β”€ movie.db                  # SQLite database (WAL mode)
β”œβ”€β”€ thumbnails/               # Downloaded poster images
└── json/
    β”œβ”€β”€ movie/                # Per-movie JSON metadata
    β”œβ”€β”€ tv/                   # Per-show JSON metadata
    └── history/              # Move operation logs (RFC3339)

Documentation sync

The install snippet shown at the top of this README is the canonical source of truth. QUICKSTART.md and spec/03-general/01-install-guide.md are regenerated from it (between <!-- INSTALL:BEGIN --> / <!-- INSTALL:END --> sentinels) by scripts/sync-install-from-readme.sh. CI runs --check on every push.

npm run sync:install              # rewrite mirrored docs from README
npm run sync:install:check        # exit 1 if any mirror has drifted
npm run sync:install:print        # preview the extracted block
npm run sync:install:json         # machine-readable output (see schema below)
npm run sync:install:list         # show resolved target list
npm run sync:install:discover     # auto-find every *.md with the sentinels
--json output schema (stable contract)

bash scripts/sync-install-from-readme.sh --json writes a single JSON object to stdout (diagnostics go to stderr, so the stream is safe to pipe into jq, $GITHUB_OUTPUT, or any tool). The schema is additive β€” new keys may be added in future versions, but existing keys, types, and meanings will not change without a major version bump.

Field Type Required Description
source string yes Always the literal "README.md" (the canonical source file).
extracted string (enum) yes How the block was located. One of: "sentinels" (preferred β€” found via <!-- README-INSTALL:BEGIN/END -->) or "heuristic" (fallback β€” matched the install heading + table).
lines integer yes Number of newline-terminated lines in block. Always β‰₯ 1.
bytes integer yes UTF-8 byte length of block. Always β‰₯ 1.
sha256 string (hex) yes Lowercase hex SHA-256 of block. 64 chars. Use this for drift detection in CI without diffing.
targets array<string> yes Resolved sync targets, as repo-root-relative POSIX paths. May be empty if the user passed --targets "". Order is significant (matches resolution order).
block string yes The verbatim install block extracted from the README, with leading/trailing blank lines trimmed. May contain newlines, Markdown, HTML, and emoji.
Required vs optional fields

Required (always present, never null, never omitted):

  • source β€” always the literal string "README.md".
  • extracted β€” always one of the enum values below.
  • lines β€” integer, >= 1.
  • bytes β€” integer, >= 1.
  • sha256 β€” string, exactly 64 lowercase hex chars (^[0-9a-f]{64}$).
  • targets β€” array of strings (possibly empty [], but the key is always present).
  • block β€” non-empty string.

Optional: none. The current schema has no optional fields. Future additive fields (introduced without a major version bump) will be documented here and MUST be treated as optional by consumers β€” assume the key may be absent on older versions.

Nullability: no field is ever null. Consumers that encounter null for any documented field should treat the output as malformed.

Allowed enum values:

  • extracted β€” exactly one of:
    • "sentinels" β€” block was located via the canonical <!-- README-INSTALL:BEGIN --> / <!-- README-INSTALL:END --> markers in README.md. This is the preferred path.
    • "heuristic" β€” sentinels were not found; the block was located by matching the install heading + first </table> + trailing <sub> caption. Treat this as a soft warning that the README should be updated to add sentinels.
  • source β€” currently always "README.md". Reserved as a string enum so future versions could extend it; consumers should still match exactly "README.md" today.

Unknown fields: consumers MUST ignore any keys not listed in the table above so the schema can grow additively.

Exit codes: 0 on success, 2 if the install block could not be extracted or python3/python is not on PATH (required to safely JSON-encode the block).

Example consumer (jq):

# Fail CI if the README install block has changed since the last release.
EXPECTED=abc123...   # sha256 from previous run
ACTUAL=$(bash scripts/sync-install-from-readme.sh --json | jq -r .sha256)
[[ "$EXPECTED" == "$ACTUAL" ]] || { echo "install block drifted"; exit 1; }

Example output (truncated):

{
  "source": "README.md",
  "extracted": "sentinels",
  "lines": 25,
  "bytes": 653,
  "sha256": "47a8bdd9900a…",
  "targets": [
    "QUICKSTART.md",
    "spec/03-general/01-install-guide.md"
  ],
  "block": "**πŸš€ Install in 10 seconds β€” anyone, any OS:**\n\n<table>\n…"
}

Milestones

Project milestones are tracked in MILESTONES.md at the repository root.

  • Location β€” MILESTONES.md (repo root, version-controlled)

  • Timezone β€” Malaysia time (UTC+8, Asia/Kuala_Lumpur)

  • Timestamp format β€” dd-MMM-YYYY hh:mm AM/PM (e.g. 24-Apr-2026 03:33 PM)

  • Entry format β€” one bullet per line under the ## Log heading:

    - <event> <dd-MMM-YYYY hh:mm AM/PM> β€” <short note>
    

Example entries:

- let's start now 24-Apr-2026 03:33 PM β€” milestone tracker initialized
- run 24-Apr-2026 07:21 PM β€” app run logged

New entries are appended to the end of the ## Log section. Generate the timestamp with:

TZ='Asia/Kuala_Lumpur' date '+%d-%b-%Y %I:%M %p'
Listing & filtering milestones

The movie milestones command reads MILESTONES.md and prints entries with optional date / keyword filters:

movie milestones                              # show all entries
movie milestones --keyword scan               # case-insensitive substring
movie milestones --date 2026-04-24            # only entries on this day
movie milestones --since 2026-04-01           # entries on/after this day
movie milestones --since 2026-04-01 -k run -n 20

Flags: --date YYYY-MM-DD, --since YYYY-MM-DD, -k/--keyword TEXT, -n/--limit N (0 = no cap).

One-shot helper (append + version bump + commit)

The repo ships with a script that appends a new milestone, bumps the patch version in version/info.go, and creates a single git commit covering both changes:

Linux / macOS

scripts/log-milestone.sh                       # default: "- run <ts> β€” app run logged"
scripts/log-milestone.sh "kickoff complete"    # custom note
scripts/log-milestone.sh --event start "kickoff"

Windows (PowerShell)

pwsh scripts/log-milestone.ps1
pwsh scripts/log-milestone.ps1 -Note "kickoff complete"
pwsh scripts/log-milestone.ps1 -Event start -Note "kickoff"

Wire it into the app (e.g. at the end of movie startup, or as a make run target) to get a milestone + commit on every run. Commit message format: chore(milestone): <event> <timestamp> β€” <note> (<new-version>).


Dependencies

Package Purpose
github.com/spf13/cobra CLI framework
modernc.org/sqlite Pure-Go SQLite driver (no CGo)

🀝 Contributing

Contributions are welcome! Here's how to get started:

  1. Fork the repository
  2. Create a branch for your feature or fix:
    git checkout -b feature/my-feature
    
  3. Follow the coding guidelines in spec/01-coding-guidelines/
  4. Keep files small β€” one file per command, max ~200 lines
  5. Run tests before submitting:
    make tidy
    go test ./... -v
    
  6. Open a Pull Request against main with a clear description
Development Setup
git clone https://github.com/alimtvnetwork/movie-cli-v7.git
cd movie-cli-v7
make tidy
make build

See the Install Guide for full setup instructions.

Code Style
  • Go standard formatting (gofmt)
  • Descriptive variable names, no abbreviations
  • Error messages start lowercase, no trailing punctuation
  • All new code must pass go vet and golangci-lint

πŸ“œ Code of Conduct

We are committed to providing a welcoming and inclusive experience for everyone.

Our Standards:

  • Be respectful, constructive, and empathetic
  • Welcome newcomers and help them get started
  • Accept constructive criticism gracefully
  • Focus on what's best for the project and community

Unacceptable Behavior:

  • Harassment, trolling, or personal attacks
  • Publishing others' private information
  • Any conduct that would be considered inappropriate in a professional setting

Enforcement: Project maintainers may remove, edit, or reject contributions that violate this code. Repeated violations may result in a temporary or permanent ban.

This Code of Conduct is adapted from the Contributor Covenant v2.1.


A system architect with 20+ years of professional software engineering experience across enterprise, fintech, and distributed systems. His technology stack spans .NET/C# (18+ years), JavaScript (10+ years), TypeScript (6+ years), and Golang (4+ years).

Recognized as a top 1% talent at Crossover and one of the top software architects globally. He is also the Chief Software Engineer of Riseup Asia LLC and maintains an active presence on Stack Overflow (2,452+ reputation, member since 2010) and LinkedIn (12,500+ followers).

Website alimkarim.com Β· my.alimkarim.com
LinkedIn linkedin.com/in/alimkarim
Stack Overflow stackoverflow.com/users/361646/alim-ul-karim
Google Alim Ul Karim
Role Chief Software Engineer, Riseup Asia LLC
Riseup Asia LLC

Top Leading Software Company in WY (2026)

Website riseup-asia.com
Facebook riseupasia.talent
LinkedIn Riseup Asia
YouTube @riseup-asia

License

Released under the MIT License β€” free for personal and commercial use, with no warranty.

Built with ❀️ by Md. Alim Ul Karim · Riseup Asia LLC

Documentation ΒΆ

Overview ΒΆ

main.go β€” entry point.

Directories ΒΆ

Path Synopsis
Package apperror provides standardized error wrapping for the CLI.
Package apperror provides standardized error wrapping for the CLI.
Package cleaner handles filename cleaning and metadata extraction from file names.
Package cleaner handles filename cleaning and metadata extraction from file names.
changelog.go β€” implements the `movie changelog` command.
changelog.go β€” implements the `movie changelog` command.
action_history.go β€” ActionHistory table: types and helpers.
action_history.go β€” ActionHistory table: types and helpers.
checks.go β€” individual diagnostic checks.
checks.go β€” individual diagnostic checks.
Package errlog provides centralized error logging to both file and database.
Package errlog provides centralized error logging to both file and database.
Package templates provides embedded HTML templates for the movie CLI.
Package templates provides embedded HTML templates for the movie CLI.
Package tmdb provides a client for The Movie Database (TMDb) API.
Package tmdb provides a client for The Movie Database (TMDb) API.
Package updater implements the copy-and-handoff self-update mechanism.
Package updater implements the copy-and-handoff self-update mechanism.
Package version holds the three build-time variables that describe exactly which binary is running.
Package version holds the three build-time variables that describe exactly which binary is running.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL