Skip to main content

A fast, local video duplicate detector

Project description

VidDup ๐ŸŽฌ

A fast, local video duplicate detector โ€” finds re-encoded, re-scaled, and compressed copies that other tools miss.

CI PyPI Python License: MIT


Why VidDup?

Most duplicate detectors only find byte-for-byte identical files. VidDup goes further:

Scenario dupeGuru Czkawka VidDup
Exact copy (same file) โœ… โœ… โœ…
Re-encoded (H.264 โ†’ HEVC) โŒ โŒ โœ…
Re-scaled (1080p โ†’ 720p) โŒ โŒ โœ…
Compressed (high โ†’ low bitrate) โŒ โŒ โœ…
Incremental cache (skip unchanged files) โŒ โŒ โœ…
Interactive HTML report with thumbnails โŒ โŒ โœ…

VidDup uses perceptual frame hashing (pHash) across multiple sampled frames per video, with a low-variance frame filter to ignore black/fade frames that cause false positives.


Quick Start

# Install system dependency
brew install ffmpeg          # macOS
# sudo apt install ffmpeg   # Ubuntu/Debian

# Install VidDup
pip install viddup

# Scan a directory
viddup scan ~/Movies

That's it. A terminal report is printed and an interactive HTML report (with video thumbnails) is automatically saved to the scanned directory and opened in your browser.


Features

  • Three-layer detection
    • L1: xxHash3-128 โ€” instant exact-copy detection
    • L2: Duration metadata โ€” groups candidates, skips impossible pairs
    • L3: 10-frame pHash โ€” perceptual similarity across re-encodes and re-scales
  • Robust pHash comparison โ€” uses median Hamming distance (not average), resistent to black frames, fade-outs, and title cards
  • Smart frame selection โ€” automatically skips solid-color frames and retries with alternate timestamps
  • Incremental SQLite cache โ€” fingerprints are cached; re-scanning a library of 500 videos takes seconds after the first run
  • Multi-directory scan โ€” finds duplicates across multiple folders in one pass
  • .viddup_ignore โ€” exclude directories and files with glob patterns, like .gitignore
  • Hardware acceleration โ€” automatically uses VideoToolbox on macOS (Apple Silicon & Intel)
  • Interactive HTML report โ€” embedded thumbnails, one-click delete-script generation, copy to clipboard

Installation

Requirements

  • Python 3.11+
  • ffmpeg (system binary)
# macOS
brew install ffmpeg
pip install viddup

# Ubuntu / Debian
sudo apt install ffmpeg
pip install viddup

# From source (development)
git clone https://github.com/your-username/viddup.git
cd viddup
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

Usage

viddup scan โ€” find duplicates

# Basic scan
viddup scan ~/Movies

# Scan multiple directories (finds cross-folder duplicates)
viddup scan ~/Movies ~/Downloads/Videos

# Stricter threshold (only report very similar videos)
viddup scan ~/Movies --threshold 0.92

# Looser duration filter (catches clips with trimmed intros)
viddup scan ~/Movies --duration-tol 0.15

# Preview what would be scanned (no DB writes)
viddup scan ~/Movies --dry-run

# Save report to a specific directory
viddup scan ~/Movies --output ~/Desktop

# Don't auto-open the browser
viddup scan ~/Movies --no-open

# Force re-fingerprint everything (ignore cache)
viddup scan ~/Movies --no-cache

Full options:

Options:
  -t, --threshold FLOAT         Similarity threshold (0โ€“1). Default: 0.85
  -f, --frames INTEGER          Frames sampled per video. Default: 10
  --duration-tol FLOAT          Duration tolerance for pre-filter. Default: 0.05 (ยฑ5%)
  --db PATH                     Fingerprint database path. Default: ~/.viddup/fingerprints.db
  -w, --workers INTEGER         Parallel worker processes. Default: CPU count
  -o, --output PATH             Report output directory. Default: first scanned directory
  --no-cache                    Ignore cache, recompute all fingerprints
  --recursive / --no-recursive  Recurse into subdirectories. Default: on
  --dry-run                     List files to be processed without writing DB
  --html / --no-html            Generate interactive HTML report. Default: on
  --open / --no-open            Auto-open HTML report in browser. Default: on
  -v, --verbose                 Show per-file progress

viddup status โ€” cache info

viddup status
# Shows: cached video count, orphan records, database size, last scan time

viddup clear โ€” clean up cache

# Remove records whose files no longer exist
viddup clear --orphans-only

# Wipe the entire cache
viddup clear --confirm

.viddup_ignore

Place a .viddup_ignore file in any scan directory to exclude files or subdirectories:

# Lines starting with # are comments

# Exclude directories (trailing slash required)
BRaw/
ๅŽŸๅง‹็ด ๆ/
proxies/

# Exclude by filename glob
temp_*
*.tmp
._*

# Exclude by relative path glob
Backups/**
archive/2020/**

VidDup reports how many files were filtered when an ignore file is active.


How It Works

Each video goes through three layers:

 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
 โ”‚  L1 โ€” File Hash (xxHash3-128)                    โ”‚
 โ”‚  Identical bytes โ†’ immediate match, skip L2/L3  โ”‚
 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                            โ”‚ not exact
 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
 โ”‚  L2 โ€” Metadata Pre-filter (ffprobe)              โ”‚
 โ”‚  Duration difference > 5% โ†’ skip pair            โ”‚
 โ”‚  Reduces O(nยฒ) comparisons to small groups       โ”‚
 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                            โ”‚ duration match
 โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
 โ”‚  L3 โ€” Perceptual Frame Hash (pHash)              โ”‚
 โ”‚  Sample 10 frames at 5%, 15%, ..., 95%           โ”‚
 โ”‚  Skip low-variance frames (black/solid color)    โ”‚
 โ”‚  Compute pHash per frame โ†’ compare sequences     โ”‚
 โ”‚  Median Hamming distance < threshold โ†’ duplicate โ”‚
 โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

"Suggest keep" priority (shown in HTML report):

  1. Highest resolution (width ร— height)
  2. Largest file size (less compression loss)
  3. Alphabetical path (deterministic tiebreak)

HTML Report

After scanning, VidDup generates a self-contained viddup_report_*.html file saved directly in the scanned directory. No server required โ€” open it with any browser.

The report includes:

  • Video thumbnails (extracted from the middle of each file, embedded as base64)
  • Grouped duplicate cards sorted by reclaimable space
  • "Suggest keep" badge for the recommended file in each group
  • Checkboxes to select files for deletion (non-suggested files pre-selected)
  • Generate delete script โ†’ shell commands you can review and execute in your terminal
  • Copy to clipboard button

โš ๏ธ VidDup never deletes files automatically. You always review the generated rm commands before executing them.


Performance

Fingerprinting speed is limited by ffmpeg frame extraction (I/O + decode) and scales linearly with video count. The main bottleneck is the first scan of a new library โ€” subsequent scans reuse the SQLite cache and complete in seconds regardless of library size.

Tips for faster scans:

  • macOS: VideoToolbox hardware decoding is enabled automatically โ€” no action needed.
  • Reduce --frames (e.g. --frames 6) for a 40% speedup with minimal accuracy loss on long videos.
  • Increase --workers on machines with many CPU cores.
  • Use --duration-tol conservatively โ€” a tighter window means fewer L3 comparisons.

Configuration Reference

Setting Default Notes
--threshold 0.85 Lower = more matches (more false positives). Try 0.90โ€“0.95 for stricter.
--frames 10 More frames = slower but more accurate. 6 is usually sufficient for short clips.
--duration-tol 0.05 Increase to 0.10โ€“0.15 if you expect trimmed intros/outros.
--workers CPU count Reduce if your system slows down during scanning.

Limitations

  • No semantic understanding: VidDup cannot detect if two videos are "about" the same topic but visually different (e.g., two different recordings of the same lecture).
  • Duration pre-filter: Videos differing by more than --duration-tol will not be compared at L3, even if they are perceptually similar. Increase the tolerance if needed.
  • Corrupt files: Videos that ffprobe cannot parse are skipped and reported in the terminal output.

Contributing

Contributions are welcome! See CONTRIBUTING.md for guidelines.

git clone https://github.com/Pengfei-Kou/viddup.git
cd viddup
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest          # run tests
ruff check .    # lint

Roadmap

  • v0.3: BK-tree for large libraries (10,000+ videos), audio fingerprint verification, .viddup_ignore negation patterns
  • v1.0: PyQt desktop GUI option, PyPI publishing, Windows testing

License

MIT ยฉ VidDup Contributors

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

viddup-0.1.0.tar.gz (110.7 kB view details)

Uploaded Source

Built Distribution

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

viddup-0.1.0-py3-none-any.whl (31.4 kB view details)

Uploaded Python 3

File details

Details for the file viddup-0.1.0.tar.gz.

File metadata

  • Download URL: viddup-0.1.0.tar.gz
  • Upload date:
  • Size: 110.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for viddup-0.1.0.tar.gz
Algorithm Hash digest
SHA256 35845411b767a61bb31d589447de68cde1e17fc217d1c719c2543f2797060e76
MD5 86e5a2293ab99eb68818e797d781f8c7
BLAKE2b-256 38cfc243a9b7d4bb22454017d22d30981dfa80431f3d586ee34b79b7a63779ce

See more details on using hashes here.

File details

Details for the file viddup-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: viddup-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 31.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.3

File hashes

Hashes for viddup-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 63cf562599a1c01cb083dc27f8a040b69b6a7fbc0283429b2060101b80a99198
MD5 f95c76e292cbda45b12adef95497f033
BLAKE2b-256 8d4e77e76d87f41aef76f10970bd9bd53ca3089ad855bbf8d80a2f504d8a0411

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page