github Xentraxx/GooglePhotosTakeoutHelper_Neo v6.2.0

latest release: v6.2.1
4 hours ago

🐛 Bug Fixes

  • Shortcut mode no longer retains physical album videos — In --albums=shortcut mode, .MP4 and .MOV companion files (and other non-image media) were left as regular files in the Albums/ folder instead of being replaced by symlinks to the canonical file in ALL_PHOTOS. The root cause was the shortcut reuse check: it treated any existing filesystem entry at the target name (file, link, or directory) as a reusable shortcut, so a stale physical file from a previous or interrupted run was silently kept instead of being replaced by a link. Shortcut reuse now only accepts actual symbolic links (Link.exists()), never regular files. When a regular file is found at the intended shortcut location, identical duplicates (same content hash as the canonical ALL_PHOTOS file) are removed, and distinct files are preserved under a Shortcut Conflicts/ subfolder before the expected link is created — so Albums/ contains only shortcuts in shortcut mode. A new ShortcutIntegrityService runs after the EXIF write phase (Step 7) to verify and restore any album shortcuts that later stages may have broken or turned back into regular files: missing links are recreated from the entity's canonical output file, broken links are deleted and relinked, and unexpected regular files are moved to Shortcut Conflicts/ before the link is restored. This resolves issue #143.

Performance

  • Removed the Pool(1) mutex from MediaHashService cache access — Every LRU cache read and write in MediaHashService was serialized through a single-resource Pool(1) lock, adding a pool-scheduling hop to every hash lookup and store. Dart's cooperative async model is single-threaded per isolate, so map access between await points is already atomic — the lock provided no safety benefit and was a serialization point across all concurrent hash operations in Step 3 (merge/duplicate detection). Removing it eliminates the scheduling overhead for every calculateFileHash, calculateHashAndSize, and cache lookup call. For a 50,000-file takeout this removes ~100,000 pool scheduling cycles during Step 3.
  • Eliminated double directory traversal in Step 2 discovery — The discovery step traversed every year and album directory twice: once to count media files (for the progress bar total) and again to actually collect them. Both passes performed the same recursive directory.list() + 512-byte header read + MIME detection per file. The count pass has been removed entirely; files are now collected in a single traversal per directory, and the progress bar total is derived from the collected list. The bar now advances during the partner-sharing JSON batch phase (the slower part of discovery) instead of during file collection. For a 50,000-file takeout this eliminates ~50,000 redundant file header reads and halves the number of recursive directory listings.

🛠️ Maintenance

  • Regression test suites for issue #143 — Integration tests (test/integration/media_entity_moving_strategies_test.dart) cover HEIC, MP4, and MOV shortcut output (each album entry must remain a symlink), missing-link restoration after later processing stages, and physical-video recovery (a stale regular file at the shortcut location is removed when identical to the canonical file, or preserved under Shortcut Conflicts/ when distinct).

What's Changed

  • Fix shortcut mode retaining physical album videos by @jaimetur in #144

Full Changelog: v6.1.10...v6.2.0

Don't miss a new GooglePhotosTakeoutHelper_Neo release

NewReleases is sending notifications on new releases.