ποΈ CHANGELOG
Planned Roadmap for the following releases
Changelog for the past releases
Release: v4.0.0
Release Date: 2026-03-28
π¨ Breaking Changes:
π New Features:
- Added full NextCloud Photos integration (WebDAV-based) across CLI, execution modes, automatic migration, and web interface (Issue: #567).
- Added a dedicated
NextCloud Photostab in the web interface, placed betweenImmich PhotosandOther Features(Issue: #567). - Added
ClassNextCloudPhotosbackend service with album/assets upload, download, cleanup, and rename/remove workflows. - Added Google Photos integration (official Library API based) across CLI, execution modes, automatic migration, and web interface.
- Added a dedicated
Google Photostab in the web interface (next toGoogle Takeout). - Added Administration Panel for admin users to create/edit/delete users and configure per-user subpaths for
/app/dataand/app/volumes. - Added per-user
Config.inipersistence in SQLite database, encrypting sensitive values at rest. - Added structured Configuration File tab editor based on sections/fields (Google Takeout, Google Photos, Synology Photos, Immich Photos, NextCloud Photos, TimeZone) with per-field help extracted from config comments.
- Added a new
Upload to Servertab in the web interface with destination-folder picker, separateUpload Local FolderandUpload Local Zipactions, and optional ZIP extraction mode (Extract ZIPs on upload: Yes/No). - Added
ClassGooglePhotosbackend service with OAuth refresh-token auth and supported upload/download modules. - Added secure multi-user mode in Docker Web Interface with login/session authentication and bootstrap admin credentials (
admin/admin123by default). - Added a dedicated
Background Progresspanel in Automatic Migration Live Dashboard to render Local Folder analysis progress (folder scan + files scan in current folder) without polluting the logs panel. (Issue #1037)
π Enhancements:
- Extended automatic migration endpoint parsing in web UI to support
nextcloud[-photos][-1..3]for both source and target. - Added Google Photos OAuth credentials support in
Config.iniand config loader ([Google Photos]section with account 1/2/3). - Extended automatic migration endpoint parsing in web UI to support
google[-photos][-1..3]for both source and target. - Updated CLI/source-target validation and help text to include
nextcloudandgoogle-photosas cloud clients. - Improved name-pattern handling across cloud modules: wildcard-only patterns like
*are now accepted for album selection, and literal patterns with special regex characters (for example album names containing parentheses) now work for matching/replacing without requiring manual escaping. - Improved album rename matching for literal album names containing regex metacharacters (such as parentheses), so rename workflows work without manual escaping.
- Improved markdown render to detect italic font and bold+italic.
- Improved album download progress visualization for cloud clients (NextCloud, Synology, Immich, Google Photos) by showing a nested per-album assets progress bar labeled as
Downloading '<AlbumName>' Assets. - Improved album upload progress visualization for cloud clients (NextCloud, Synology, Immich, Google Photos) by showing a nested per-album assets progress bar labeled as
Uploading '<AlbumName>' Assets. - Restricted folder/file browser API access to each user's assigned subfolders only, and enforced the same path restrictions at command execution time.
- Enhancements in Web UI.
- Forbidden Import/Export/Save configuration file in demo roles.
- Added NextCloud dual-folder configuration in
Config.iniand config loader with per-accountNEXTCLOUD_PHOTOS_FOLDER_<id>(assets/no-albums) andNEXTCLOUD_ALBUMS_FOLDER_<id>(folder-based albums). - Separated markdown render in a new static file
- Smoothed Automatic Migration Live Dashboard rendering by switching to manual refresh-on-change updates (dirty panels only), stabilizing Background Progress row order, and reducing completion-row churn to minimize panel flicker/tremor in CLI.
- Added vertical scroll to the
Access Logstable in the Administration Panel, limiting visible height (about 20 recent entries) while keeping older entries accessible by scrolling. - Improved
Background Progresslabel normalization to keep only text after:, trim trailing:variants (:,:,:), and remove trailing path clauses such asin .../in folder ...(Linux/Windows paths, quoted or unquoted). - Improved
Background Progresstitle normalization to keep only the text after:(when present) and then trim surrounding whitespace. - Improved CLI Live Dashboard Background Progress routing for Takeout post-processing
tqdmlines by accepting additional prefixes (includingTQDM ...) and indeterminate progress frames (for example81 files [00:00, ...]) so those updates are rendered in theBackground Progresspanel instead of the logs panel. - Improved CLI Live Dashboard responsiveness on terminal resize by syncing layout dimensions on each refresh cycle and recalculating visible
Logs Panelrows from the current panel height. - Refined CLI Live Dashboard log coloring rules to style only explicit Automatic Migration events (
Asset Pulled,Asset Pushed,Asset Duplicated,Album Created,Album Pulled,Album Pushed,Asset Fail/Failed) instead of broad keyword matches. - Updated GPTH from version 5.0.5 to version 6.1.1 which includes several enhancements and bug fixing.
π GPTH Enhancements:
6.1.1
- Added
archiveras correct french translation of archive.
6.1.0
- Added
--all-photos-dirCLI option to customize the non-album output directory name (default remainsALL_PHOTOS). Set it to an empty string (--all-photos-dir "") to remove that extra directory level entirely. This makes album links more portable when migrating into existing folder structures. - Added
--hardlinkflag (Windows only) forshortcutandreverse-shortcutalbum modes. When enabled, GPTH creates hard links instead of symlinks for shortcut entries. --transform-pixel-mpnow accepts an explicit output format:mp4,jpg, orstill.- Step 6 Pixel motion-photo transformation now supports two modes:
mp4: rename.MP/.MVprimary files to.mp4.jpg: create motion.jpgfiles from Pixel motion photos.still: keep only a still image (prefers sidecar*.MP.jpg, otherwise extracts embedded JPEG) and remove related.MP/.MVsource files.
β οΈ --transform-pixel-mp jpgis currently preview/experimental and may be unstable depending on source file structure.- Step 1: Pixel Motion Photo files (.MP, .MV) no longer unconditionally converted to .mp4 β Pixel Motion Photo files have
video/mp4MIME type but.MP/.MVextensions. Previously, Step 1 unconditionally renamed them to.mp4due to the MIME/extension mismatch, making the--transform-pixel-mpflag ineffective. Step 1 now preserves.MP/.MVfiles, deferring to Step 6 which respects the flag: with--transform-pixel-mp, they are converted to.mp4; without the flag, they are left as-is.
6.0.0
- Added progress bar to Step 5 (Find Albums) to show album association processing progress
- Step 1: Extension fixing now replaces the incorrect extension instead of appending β Previously, a file like
vacation_sunset.heic(actually JPEG) would be renamed tovacation_sunset.heic.jpg. Now it becomesvacation_sunset.jpg. The associated JSON sidecar and any supplemental-metadata JSON files are atomically renamed to match. This produces cleaner output filenames with no change in metadata accuracy, since all downstream steps already used only the final extension. The double-extension handling in the truncated filename fixer (Step 4) has been kept for natural Pixel-style suffixes (.PANO.jpg,.MP.mp4, etc.) which are not affected by this change. - Step 3 progress bar now fills in real time β Previously the hashing phase (
groupIdenticalFast2) only printed a text message every 50 size groups (and only in verbose mode), so the progress bar appeared to jump to 100% instantly at the end. AFillingBaris now created before the bucket-processing loop and updated after each slice of size groups finishes, giving continuous visual feedback during the (potentially long) deduplication hashing phase. - Step 7 progress bar unified β The two separate bars ("Writing EXIF data" and "Flushing pending EXIF writes") are now a single bar that tracks all output files from start to finish. The total is pre-counted before processing begins, so the bar fills steadily across the whole step without a surprise second bar appearing at the end.
- Overall pipeline performance is approximately 3Γ faster on a modern PC with an SSD compared to the previous version, based on real-world tests (400GB takeout now 38m instead of 1h 20m) due to the following changes:
- Step 1 (Fix Extensions): single-pass collection + parallel processing β Previously the directory was traversed twice: once to count files (for the progress bar) and once to process them. A single
toList()now serves both purposes, and_processFile(128-byte header read + MIME check + optional rename) runs in parallelFuture.waitbatches atdiskOptimizedconcurrency. - Step 2 (Discover Media): parallel JSON partner-sharing checks β
jsonPartnerSharingExtractorwas called sequentially per file inside the stream loop. Files are now collected first, then processed withFuture.waitin batches ofdiskOptimizedconcurrency, reducing total I/O wait from a sum of latencies to roughly one batch-time perN/batchSizeiterations. - Step 6 (Move Files): parallel file operations β
moveAllnow calls the parallel variant of the move engine (moveMediaEntitiesParallel) withdiskOptimizedconcurrency (cores Γ 8, max 32) instead of the sequential one-entity-at-a-time loop. The parallel implementation already existed but was dead code. For cross-drive copy operations this is the single largest win. - Step 7 EXIF batch write throughput dramatically improved β
stableTagsetKeypreviously grouped files by tag names and values, so every file landed in its own 1-entry bucket (unique date string = unique key). The threshold check never fired, and the final flush called one ExifTool process per file. The batch queue now groups by tag names only; all files needing the same tag set (e.g.DateTimeOriginal + DateTimeDigitized + DateTime) land in a single bucket. ExifTool's batch mode already supports different values per file by interleaving per-file args before each filename, so correctness is unchanged. This results in a large reduction in ExifTool process spawns for typical collections. - 7-Zip extraction speed improved β The 7-Zip extractor now uses
-mmt=N(explicit thread count equal toPlatform.numberOfProcessors) instead of-mmt=on, and suppresses stdout/stderr/progress pipe output (-bso0 -bse0 -bsp0) to reduce I/O overhead. For large archives with many files this avoids unnecessary process pipe traffic and lets 7-Zip use all available CPU cores. - Step 7: ExifTool stay-open IPC β zero Perl startup overhead β All ExifTool write operations (single-file and batch) are now routed through a single long-running
exiftool -stay_open Trueprocess started once at launch. On Linux / WSL, Perl startup costs ~1-2 s per invocation; every write now takes only the actual I/O time. The argfile batch path also no longer needs a temp file when stay-open is active, as stdin has no command-line length limit. Falls back transparently to one-shot invocations if the persistent process fails to start. - Parallel ZIP extraction β When 7-Zip is available and multiple ZIPs are present, archives are now extracted concurrently. Concurrency scales with core count (
max(2, NΓ·4), capped at 4) so 4-core machines run 2 in parallel, 8-core run 3, 16-core run 4, etc. Since extraction is I/O-bound (JPEGs are already compressed, so Deflate adds negligible CPU work), each process receives the full processor count (-mmt=N) rather than a split share β threads mostly block on I/O and don't compete for CPU. Native Dart extraction remains sequential to avoid simultaneous heap pressure from two large ZIPs. - Step 7: Large MOV/MP4 files with oversized QuickTime atoms are no longer retried β ExifTool emits
atom is too large for rewritingwhen a video file's data block exceeds its internal rewrite limit (e.g. a 676 MB MOV file). Previously this produced 4β6 noisy log lines and a pointless single-file retry. The error is now recognised as unrecoverable: the batch-level "retrying" message is suppressed, no per-file retry is attempted, and a single clear[WARNING]is emitted per affected file stating that the file was still sorted correctly. - JSON sidecar read consolidated (Steps 4 + 7) β Each media file's
.jsonsidecar was previously parsed up to three times: once for the date, once for GPS coordinates, and again in Step 7 to retrieve coordinates for EXIF writing. GPS is now extracted alongside the date in a single read during Step 4 and cached on the entity, so Step 7 requires no additional file I/O for GPS data. - GPS data from
geoDataExifnow correctly used β The coordinate extractor previously only read from thegeoDatafield of the JSON sidecar. Google Photos also stores the original camera-recorded GPS ingeoDataExif, which is often the only source of valid coordinates (e.g. for videos, photos edited by third-party apps that strip EXIF, or photos tagged after upload). The extractor now prefersgeoDataExifand falls back togeoData, significantly increasing the number of files that receive GPS in their output EXIF.. - Step 3: XXH3 replaces hand-rolled FNV-1a for quick-signature and fingerprint hashing β The 32-bit FNV-1a closure used in
_quickSignatureand the 64-bit FNV-1a method used in_triSampleFingerprintare replaced by XXH3 (viapackage:xxh3). XXH3 is approximately 10Γ faster than SHA-256 and significantly faster than FNV-1a on the 4 KiB slices read per bucket candidate, while providing 64-bit hash quality. - Full-file content hashing uses XXH3 instead of SHA-256 β
MediaHashService.calculateFileHash(the definitive byte-for-byte equality check used before any file is discarded) now usesxxh3Stringfor small files and thexxh3Streamchunked API for large files. This replaces the previouspackage:cryptoSHA-256 implementation. Thepackage:cryptodependency has been removed.
- Step 1 (Fix Extensions): single-pass collection + parallel processing β Previously the directory was traversed twice: once to count files (for the progress bar) and once to process them. A single
- Step 1: Extension collision resolved with unique filename instead of skip β When fixing a file's extension would produce a name that already exists (e.g.
teams_jens.pngβteams_jens.jpgbutteams_jens.jpgalready exists due to storage-saver mode), the file is now renamed to the next available unique name (teams_jens(1).jpg) using the same(N)counter logic as the move step. Files with an existing counter suffix are handled correctly:teams_jens(1).pngβteams_jens(1)(1).jpg. Previously such files were silently left with the wrong extension, which later caused ExifTool batch failures ("Not a valid PNG β looks more like a JPEG") with a noisy multi-round binary-split cascade in Step 7. - Windows: emoji album folders no longer cause pipeline failures β On Windows,
Directory.list()throws when a path contains certain emoji characters. Album directories with emoji names (e.g.Holiday Memories π) are now temporarily renamed to a hex-encoded form at the start of the pipeline and restored immediately after all steps complete. Output album folders always use the original emoji name. If the process crashes mid-run, the hex-encoded names are detected and restored automatically on the next run viaprogress.json. - Step 1: Extension fixing no longer skips edited files by default β Files with language-specific "edited" suffixes (e.g.
-edited) were unconditionally skipped during extension fixing, regardless of the--skip-extrasflag. This meant a file likeIMG_3376-bearbeitet.HEICthat was actually a JPEG would keep its wrong extension and fail later withNot a valid HEIC (looks more like a JPEG). The guard is now conditional: edited files are only skipped during extension fixing when--skip-extrasis explicitly set. - Added
archiverenas a recognised Dutch andarchivierenas a German special folder name (Google Photos exports this as a mistranslation of "Archive" for NL/GER users). - Windows: trailing backslash in quoted paths β
--input "path\"and--output "path\"now work correctly. The trailing path separator is stripped before processing; previously the C-runtime interpreted\"as an escaped quote, causing subsequent flags to be swallowed into the path value. If the resulting path value still appears to contain embedded flags (e.g.--input "path\" --output ...), GPTH now exits with a clear diagnostic message instead of silently failing. - Suppressed a misleading batch-level ExifTool warning for InteropIFD errors. Those files are already retried individually (introduced in v5.1.1), so logging the whole batch as failed gave the false impression that every file in the batch was broken.
- Step 7: UTC offset tags now written natively for JPEGs, fixing InteropIFD corruption warnings β
OffsetTime,OffsetTimeOriginal, andOffsetTimeDigitizedare now written inside the native JPEG write methods (writeDateTimeNativeJpeg/writeCombinedNativeJpeg) together with the date tags, eliminating the second ExifTool invocation that previously followed every successful native write. This is also more resilient for files with a corrupt InteropIFD: theimagelibrary's sub-IFD reader wraps each sub-IFD in atry/catchand silently drops any that fail to parse, then the writer removes the dangling0xA005pointer β so the output JPEG has a clean EXIF block with no corrupt InteropIFD, rather than triggering ExifTool'sTruncated InteropIFD directoryerror. The ExifTool fallback path (used when the native write itself fails) is untouched and still includes the strip-and-retry logic from v5.1.1. This addresses an issue introduced in version 5.0.9, during the fix of the UTC bug. - Step 7: Large MOV/MP4 files with oversized QuickTime atoms are no longer retried β ExifTool emits
atom is too large for rewritingwhen a video file's data block exceeds its internal rewrite limit (e.g. a 676 MB MOV file). Previously this produced 4β6 noisy log lines and a pointless single-file retry. The error is now recognised as unrecoverable: the batch-level "retrying" message is suppressed, no per-file retry is attempted, and a single clear[WARNING]is emitted per affected file stating that the file was still sorted correctly. - Step 4: Truncated filename fixer no longer duplicates Pixel suffixes β Files with double extensions containing Pixel-specific suffixes (
.PANO.jpg,.MP.mp4,.NIGHT.jpg,.vr.jpg) had the suffix doubled when the truncated filename fixer restored the full name from JSON metadata (e.g.PXL_20230518_095458599.PANO.PANO.jpg). The title's extension is now stripped symmetrically with the filename's, preventing the duplication. - 7-Zip detection logged once β The 7-Zip executable path is now resolved once per extraction session (cached in the service instance) and reported via a single
[ INFO ]message. Previously the path was re-detected for every ZIP file, producing no visible confirmation at all in CLI mode. - Removed noise in verbose logs and ensured more accurate representation of errors/warnings
- Step 7: MTS, M2TS, WMV, AVI, MPEG, and BMP files are now skipped before ExifTool is called β ExifTool does not support writing metadata to these formats. Previously they were passed to ExifTool individually, producing
[WARNING] ExifTool command failednoise for every such file. They are now detected upfront by extension and MIME type and silently skipped (a single warning is still logged per file unless warnings are silenced). - Refactoring, offloading complex logic in separate files for maintainability and removed legacy code.
- Replaced custom
_Mutexclass withPool(1)frompackage:poolinMediaHashServiceβ same single-access semantics with less custom code. - Replaced hand-rolled
LinkedHashMapLRU cache (~60 lines) withLruCachefrompackage:lruinMediaHashService. - Added type-safe
toJson()/fromJson()serialization toMediaEntity,FileEntity, andAlbumEntity, replacing ~260 lines of duck-typeddynamiccasting inProgressSaverService.
5.1.1
- Fixed a bug where non-english year folder names could cause them to be classified as albums
- Fixed ExifTool failing with
Bad format (282) for InteropIFD entryorTruncated InteropIFD directoryerrors on certain images (Google Photos edited files with-editedsuffix, WhatsApp images). Root cause: the UTC timezone offset tags (OffsetTime*) introduced in v5.0.9 trigger ExifTool's IFD traversal, which aborts on files with a corrupted InteropIFD structure. Fix: when either error is detected, the offset tags are stripped and the write is retried β date and GPS data are still written successfully, matching v5.0.8 behaviour for these files. (#108) - Improved error messaging for InteropIFD failures: the per-file warning now correctly distinguishes between a UTC timezone offset tag failure (date was already written natively β no data loss) and an actual date metadata write failure. A step-level summary is printed when one or more files are affected, with a description and the total count of affected files.
5.1.0
- Upgraded mime package to 2.0.0 (contains bugfix)
- Added german and spanish "Photos from" localization.
- Fixed an issue with MacOS unicode normalisation (#99)
- Fixed a possible endless loop (#102)
- Made Exiftool discovery on Windows more robust when installed via chocolatey and not added to PATH.
- Added -editada suffix for spanish
- bumped some dependencies
- Will not allow any mode which requires symlink on a filesystem which does not support symlinks (#105)
5.0.9
- Fixed a UTC conversion bug
- Fixed that geodata was removed from exif
- fixed a bug where a path join used a unix path seperator instead of being platform agnostic.
5.0.8
- Updated upstream library to image 4.7.2 which contains fixes to the native writeExif() method.
5.0.7
- ZIP extraction no longer deletes an existing extraction directory. GPTH Neo now refuses to extract into a non-empty folder to prevent accidental deletion of unrelated files.
- Interactive mode: Added an explicit DANGER warning before confirming output directory cleanup (deletes recursively inside the chosen output folder).
- Restore truncated media filenames from JSON sidecars (uses the JSON
titlefield) after date extraction, renaming both the media file and its JSON metadata so later steps use the original name.
5.0.6
- Fixed german unknown folder name from "unbekannt" to "Unbenannt" to correctly identify unknown folders (please create a bug report if those folders are exported in your language and provide us with the correct translation)
- fixed unit tests
- fixed partner sharing logic
- Added Auto-Resume support to avoid repeat successful steps when tool is interrupted and executed again on the same output folder. (#87).
- Untitled Albums now are detected and moved to
Untitled Albumsforder. (only if albums strategy isshortcut,reversed-shortcutorduplicate-copy, the rest of albums strategies don't creates albums folders). (#86). - Upgraded exif_reader package to the newest version.
- Fixed #90 (duplicated output in interactive mode)
- Fixed major error which led to native exif write methods not being used when exiftool was not installed.
- Fixed issue with App1 marker in image library when jpg has no exif block. Using own fork of image library until pull request to the source repo is accepted. Fixes issue #95
- Minor Bug Fixing.
π Bug fixes:
- Fixed web command/help text normalization so
--client=nextcloudexamples are parsed consistently in UI descriptions. - Fixed
Automatic Migrationcloud-session initialization for NextCloud and Google Photos clients by enabling lazy thread-safe auto-login on first API call, preventingsession is not initialized. Call login() firsterrors in source/target worker flows. - Fixed
Automatic Migrationdashboard crash in frozen binaries when Rich unicode tables are missing (ModuleNotFoundError: rich._unicode_data.unicode17-0-0) by adding packaging includes forrich._unicode_dataand graceful dashboard fallback. - Fixed NextCloud
Download All/Download Assetspath behavior to scanNEXTCLOUD_PHOTOS_FOLDER_<id>recursively while excludingNEXTCLOUD_ALBUMS_FOLDER_<id>when nested, preventing duplicate album downloads and supporting direct/Photoslayouts. - Fixed
--no-log-filebehavior so Google Takeout runs no longer create the logs folder or log file when that flag is enabled. - Fixed Web Interface path validation to reject using the user's root
DATA_DIR/VOLUMES_DIRdirectly as any input/output path, requiring a subfolder and showing a warning dialog before execution. - Fixed local album linking in
Automatic Migration/LocalFoldertargets by using the correct exclusion helper and caching the created album path instead of the resolved source path, avoidingAlbum Push Failerrors such as'FolderAnalyzer' object has no attribute '_should_exclude'. - Other bug fixing.
π Documentation:
- Added NextCloud Photos help page and linked it from README/help index.
- Added Google Photos help page and linked it from README/help index.
- Updated CLI/configuration/automatic-migration docs to include NextCloud and Google Photos support.
- Changed header styles in help documents.
- Updated documentation with all changes.
πΎ Download
Download the tool either for Linux, MacOS or Windows (for both x64 and arm64 architectures) or Docker version (platform & architecture independent) as you prefer, directly from following links:
Linux::
Mac OS:
Windows:
Docker Launcher:
What's Changed
- Fixed stream Immich multipart upload to avoid OOM. by @jaimetur in #1040
- main to 3.8.0 by @jaimetur in #1042
- Fixed Exiftool command line overflowing max length by @stefan-sherwood in #1052
New Contributors
- @stefan-sherwood made their first contribution in #1052
Full Changelog: v3.7.1...v4.0.0