A Python script to intelligently replace lower-quality videos in Immich with transcoded versions while preserving all metadata, managing backups, and supporting batch processing.
Minimum Immich Version: v2.4.0+
Tested Versions: v2.4.1
Last Updated: December 28, 2025
This script requires Immich v2.4.0 or newer due to API endpoint changes. If you're running an older version of Immich, please upgrade first or use an older version of this script.
- Authentication endpoint changed from
/auth/meto/users/me - Asset metadata updates changed from
PATCHtoPUT - See API_TEST_RESULTS_FINAL.md for detailed compatibility testing
- 🔧 Fixed Immich v2.4+ compatibility - Updated authentication and metadata endpoints
- ✅ 100% API compatibility - All endpoints tested and working
- 📊 Comprehensive testing - Tested against Immich v2.4.1 demo server
- 📚 Enhanced docs - Added detailed API compatibility reports
- 🔍 Two-tier matching strategy - Exact match (auto) + fuzzy match (with confirmation)
- 🐛 Debug mode - Comprehensive API logging with
DEBUG_APIflag - 📊 Better visibility - Clear distinction between exact and fuzzy matches
- 🛠️ Bug fixes - All critical bugs resolved (see CHANGELOG.md)
✨ Smart Video Matching
- Priority: Exact match (filename + duration <0.5s) - automatic, no confirmation
- Fallback: Fuzzy match (filename + duration ±tolerance) - requires confirmation
- Interactive prompts for uncertain matches
- Detailed matching logs showing why videos matched or didn't
🎬 Intelligent Replacement
- Uses Immich's
replaceAssetAPI endpoint to maintain asset IDs - Preserves original upload dates and all metadata
- Automatically moves original videos to backup album
- Adds transcoded video to all albums the original was in
💾 Backup Management
- Automatically creates backup album if it doesn't exist
- Moves original assets to backup album before replacement
- Preserves complete asset history
📊 Progress & Reporting
- Real-time progress bar during processing
- Detailed summary statistics (exact/fuzzy matches, replaced, failed)
- Comprehensive logging to file
- Dry-run mode to preview actions before executing
🐛 Debug Mode
DEBUG_APIflag logs all API requests and responses- Helps troubleshoot connection issues and unexpected behavior
- Automatic API key redaction in logs
- See DEBUG_GUIDE.md for details
⚙️ Flexible Configuration
- All settings via
.envfile - Accept album by name or UUID
- Configurable retry policy
- Tolerance settings for fuzzy matching
- Python 3.8+
- FFmpeg/FFprobe installed
- Immich v2.4.0+ instance with API access
- Video files in a designated folder
# Clone repository
git clone https://github.com/Mr187k/immich-video-replace.git
cd immich-video-replace
# Create virtual environment
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt# Ubuntu/Debian
sudo apt install ffmpeg
# macOS
brew install ffmpeg
# Windows
choco install ffmpeg# Copy example config
cp .env.example .env
# Edit with your settings
nano .env # or your preferred editorMinimum required settings:
IMMICH_URL=http://192.168.1.100:2283/api
IMMICH_API_KEY=your_api_key_here
ALBUM_ID_OR_NAME=MyVideosGetting your API key:
- Open Immich web interface
- Settings → Account → API Keys
- Create API Key
- Copy to
.env
python test_connection.pyExpected output:
✅ ALL TESTS PASSED - Ready to use immich_video_replace.py!
If you see authentication errors, ensure you're running Immich v2.4.0 or newer.
# Ensure DRY_RUN=true in .env
python immich_video_replace.pyReview output, then set DRY_RUN=false and run again.
| Variable | Type | Default | Description |
|---|---|---|---|
IMMICH_URL |
str | - | Base URL of Immich API (include /api) |
IMMICH_API_KEY |
str | - | Immich API key for authentication |
ALBUM_ID_OR_NAME |
str | - | Album UUID or name containing original videos |
BACKUP_ALBUM_NAME |
str | "Backup - Original Videos" | Album name for storing original assets |
INPUT_FOLDER |
str | "./output" | Folder containing transcoded videos |
MATCH_DURATION_TOLERANCE_SECONDS |
int | 2 | Tolerance in seconds for fuzzy matching |
MAX_RETRIES |
int | 3 | Max retry attempts for failed uploads |
RETRY_DELAY_SECONDS |
int | 5 | Delay between retry attempts |
LOG_FILE |
str | "./immich_replace.log" | Log file path |
LOG_LEVEL |
str | "INFO" | Logging level (see below) |
DEBUG_API |
bool | false | Log all API requests/responses (true/false) |
DRY_RUN |
bool | false | Preview actions without executing (true/false) |
| Level | When to Use | What You'll See |
|---|---|---|
DEBUG |
Troubleshooting, development | All operations, matching details, API calls (if DEBUG_API=true) |
INFO |
Normal operation (recommended) | Progress, matches, successes, important events |
WARNING |
Production monitoring | Issues that didn't prevent completion |
ERROR |
Minimal logging | Only failures and critical errors |
CRITICAL |
Emergency only | Only catastrophic failures |
Recommended settings:
- First run:
LOG_LEVEL=DEBUG+DRY_RUN=true - Production:
LOG_LEVEL=INFO - Troubleshooting:
LOG_LEVEL=DEBUG+DEBUG_API=true
# 1. Test connection
python test_connection.py
# 2. Dry-run to preview
DRY_RUN=true python immich_video_replace.py
# 3. Review output and logs
cat immich_replace.log
# 4. Run for real
DRY_RUN=false python immich_video_replace.py# Edit .env:
DEBUG_API=true
LOG_LEVEL=DEBUG
# Run and check logs
python immich_video_replace.py
cat immich_replace.logThe script uses a two-tier matching approach:
- Criteria: Filename matches + duration difference < 0.5s
- Behavior: Automatically accepted (no confirmation)
- Use case: Same video, just transcoded
- Criteria: Filename matches + duration difference ≤ tolerance (default: 2s)
- Behavior: Requires user confirmation
- Use case: Transcoding changed duration slightly
See MATCHING_STRATEGY.md for complete details.
For each matched video:
- ✅ Get original metadata (description, rating, favorite, GPS location)
- ✅ Move original to backup album (removed from all existing albums)
- ✅ Replace asset with transcoded version using Immich API
- Maintains original asset ID
- Preserves original
fileCreatedAtdate - Preserves original upload timestamp
- ✅ Update metadata on new asset
- ✅ Add to all original albums (restore album membership)
- ✅ Delete local file (after successful upload)
.mp4, .mkv, .mov, .avi, .webm, .flv, .m4v
=== Video Matching ===
Strategy: 1) Exact (filename + duration <0.5s), 2) Fuzzy (filename + ±2s)
[PRIORITY] Searching exact match for "vacation.mp4" (duration: 125.42s)
✅ [EXACT MATCH] Found: "vacation.mp4" (duration diff: 0.040s)
[PRIORITY] Searching exact match for "beach_trip.mp4" (duration: 95.20s)
[PRIORITY] No exact match found
[FALLBACK] Searching fuzzy match for "beach_trip.mp4"
⚠️ [FUZZY MATCH] Found: "beach_trip.mp4" (duration diff: 1.38s)
⚠️ Fuzzy match found (requires confirmation):
Local file: beach_trip_transcoded.mp4
Immich file: beach_trip.mp4
Local duration: 95.20s
Immich duration: 96.58s
Difference: 1.38s (tolerance: ±2s)
Are these the same video? (yes/no): yes
=== Matching Summary ===
Scanned: 2
Matched: 2 (✅ 1 exact, ⚠️ 1 fuzzy)
Unmatched: 0
Proceed with replacement? (yes/no): yes
=== Processing Videos ===
Replacing videos: 100%|████████████| 2/2 [00:15<00:00, 7.5s/video]
=== Final Summary ===
Matched: 2 (✅ 1 exact, ⚠️ 1 fuzzy)
Successfully replaced: 2
Failed: 0
Moved to backup: 2
All metadata is inherited from the original video:
- ✅
fileCreatedAt- Original upload date - ✅
dateTimeOriginal- Original capture date/time - ✅
description- Video description - ✅
rating- Star rating (1-5) - ✅
isFavorite- Favorite status - ✅
latitude/longitude- GPS location data - ✅ Album membership - Restored to all original albums
Problem: Authentication failed or 404 Not Found on /auth/me
Solution: You're running Immich < v2.4.0. Either:
- Upgrade Immich to v2.4.0+ (recommended)
- Use an older version of this script (v1.0.0)
Check your Immich version:
curl http://your-immich:2283/api/server/versionProblem: Failed to connect to Immich
Solutions:
- Verify
IMMICH_URLincludes/api - Check Immich is accessible:
curl http://your-immich:2283/api/users/me - Verify API key is valid
- Run
python test_connection.pyto diagnose
Problem: Album not found: MyVideos
Solutions:
- Check album name spelling (case-sensitive)
- Use album UUID instead of name
- Run
python test_connection.pyto list available albums
For exact matches:
- Check if filename is similar (case-insensitive matching)
- Verify duration difference is < 0.5s
- Enable
LOG_LEVEL=DEBUGto see matching details
For fuzzy matches:
- Adjust
MATCH_DURATION_TOLERANCE_SECONDS(try 3-5s) - Check logs for "duration differs" messages
- Use
DEBUG_API=trueto see full asset data
See MATCHING_STRATEGY.md for detailed troubleshooting.
Solutions:
- Increase
MAX_RETRIESandRETRY_DELAY_SECONDS - Check Immich server disk space
- Verify file permissions on input folder
- Enable
DEBUG_API=trueto see exact error responses
Solutions:
- Ubuntu/Debian:
sudo apt install ffmpeg - macOS:
brew install ffmpeg - Windows: Install FFmpeg and add to PATH
- API_TEST_RESULTS_FINAL.md - Comprehensive API compatibility testing
- MATCHING_STRATEGY.md - Complete guide to video matching logic
- DEBUG_GUIDE.md - Debugging and troubleshooting guide
- CHANGELOG.md - Version history and bugfixes
- TEST_WITH_DEMO.md - Testing with demo instance
Test API compatibility with your Immich instance:
# Run automated compatibility tests
python test_api_endpoints.py
# Should show 100% compatibility for Immich v2.4+See API_TEST_RESULTS_FINAL.md for expected results.
immich-video-replace/
├── immich_video_replace.py # Main script (v1.1.1)
├── test_connection.py # Connection testing utility
├── test_api_endpoints.py # API compatibility tester
├── requirements.txt # Python dependencies
├── .env.example # Example configuration
├── .env.test # Demo instance test config
├── .gitignore # Git ignore rules
├── README.md # This file
├── CHANGELOG.md # Version history
├── MATCHING_STRATEGY.md # Matching algorithm guide
├── DEBUG_GUIDE.md # Debug mode documentation
├── TEST_WITH_DEMO.md # Testing guide
├── API_TEST_RESULTS_FINAL.md # API compatibility report
└── API_COMPATIBILITY_REPORT.md # Detailed endpoint analysis
A: Immich v2.4.0 and newer. Tested with v2.4.1. Run test_api_endpoints.py to verify compatibility.
A: No! Originals are moved to a backup album. You can delete them later if desired.
A: Yes, restore the original from the backup album manually in Immich.
A: Videos are replaced one at a time. Already replaced videos won't be re-processed. Check logs and re-run.
A: Currently one album per run. Run the script multiple times with different ALBUM_ID_OR_NAME values.
A: No, only video files are processed.
A: In Immich web UI: Settings → Account → API Keys → Create API Key
A: Successfully uploaded files are automatically deleted from the INPUT_FOLDER.
A: Check your Immich version. This script requires v2.4.0+. See "Compatibility Issues" in Troubleshooting.
The script includes multiple safety mechanisms:
- ✅ Version Check - Warns if Immich version is incompatible
- ✅ Dry-Run Mode - Preview all changes before executing
- ✅ Backup Album - Originals moved to backup, not deleted
- ✅ Metadata Preservation - All metadata inherited by new video
- ✅ User Confirmation - Ask before fuzzy matches
- ✅ Comprehensive Logging - Full audit trail of operations
- ✅ Error Handling - Graceful failure with detailed error messages
- ✅ Retry Logic - Automatic retries for transient failures
- ✅ Connection Testing - Verify setup before replacement
Having issues? Follow these steps:
- Check Immich version: Must be v2.4.0+
- Run
python test_api_endpoints.pyto verify API compatibility - Run
python test_connection.pyto verify connectivity - Enable debug mode:
DEBUG_API=true+LOG_LEVEL=DEBUG - Check API_TEST_RESULTS_FINAL.md for known issues
- Check DEBUG_GUIDE.md for troubleshooting
- Review MATCHING_STRATEGY.md for matching issues
- Check log file for detailed error messages
- Open an issue with sanitized logs (remove sensitive data)
| Script Version | Immich Version | Status |
|---|---|---|
| v1.1.1+ | v2.4.0+ | ✅ Fully Compatible |
| v1.1.0 | v2.4.0+ | |
| v1.0.0 | < v2.4.0 |
MIT License - feel free to use and modify as needed.
Issues, bug reports, and pull requests welcome!
⚠️ Test on a backup of your Immich library first⚠️ Ensure you're running Immich v2.4.0 or newer- ✅ Always enable dry-run mode for initial testing
- ✅ Verify configuration carefully before running
- ✅ Run
test_api_endpoints.pyto verify compatibility - ✅ This script modifies your Immich library; ensure backups exist
Happy video transcoding! 🎬✨