Introduction
Aegis Photo Voyager features a high-performance, non-blocking Smart Photo Importer. Unlike cloud-based tools that stream your data to third-party servers, Photo Voyager copies and indexes your photos completely offline, utilizing advanced multi-threaded background workers to maintain a fast, responsive user interface.
This tutorial walks through the technical details of how the import workflow operates under the hood, how to connect physical or digital sources, and how the importer initiates the offline AI enrichment pipeline.
1 Import Sources
The Smart Importer supports three main categories of ingestion sources, accessible as cards in the user interface:
1. External SSDs, HDDs & Local Folders
You can ingest media from external hard drives, SD cards, or any local folder directory. When a folder path is selected:
- The application queries filesystem metadata using
get_drive_info(path). - A unique Drive ID and Drive Name are recorded in your local SQLite database.
- This enables Photo Voyager to remember that specific physical drive and skip scanning files that have already been imported from it in the past.
2. Google Photos & Apple iCloud ZIP Archives
Ideal for migrating large archives out of cloud services (e.g., using Google Takeout or Apple Data Export):
- Select the
.ziparchive in the importer UI. - The application spawns a secure, temporary directory prefixed with
aegis_import_. - The ZIP file is extracted, scanned, and media files are safely imported.
- Once the import completes or aborts, the temporary directory is automatically deleted to free up local disk space.
3. iPhone / Mobile Devices (USB)
Direct mobile imports leverage platform-specific connection layers to bridge mobile storage with your PC:
| OS | Subsystem & Mechanism | Behavior |
|---|---|---|
| Linux | GVFS Mount / ifuse / gphoto2 |
The app runs idevicepair pair and mounts the device to ~/iphone_media using ifuse. If unavailable, it falls back to querying GVFS mounts under /run/user/<UID>/gvfs/ or detecting cameras with gphoto2. Mounts are cleanly unmounted on exit. |
| macOS | pymobiledevice3 via usbmux |
Queries iOS device lockdown services directly to acquire metadata like DeviceName and UniqueDeviceID, then streams selected media files over high-speed USB. |
Mobile Untrusted / Locked State: If your mobile device is locked or has not trusted the computer, connection scripts will fail. Make sure to wake the device, enter your passcode, tap "Trust This Computer", and click retry in the import window.
2 Ingest Modes & Filtering
Ingestion in Aegis Photo Voyager is highly customizable. You can configure filters and organizational rules in the dialog before starting an import:
Date Filtering Modes
To avoid reprocessing files that are already indexed in your master library, choose one of these three ingest modes:
| Mode | Functional Behavior | Ideal Use Case |
|---|---|---|
| All Photos | Scans and attempts to ingest every file in the source directory. Duplicates are resolved via perceptual hashing. | First-time setup, migrating an entire archive, or bulk folder imports. |
| Since Last Import | Queries the SQLite database for the timestamp of the last successful import from the current drive_id or iphone_id. Only files captured after that date are copied. |
Routine incremental backups (e.g. hooking up your phone or camera card every week). |
| Since Custom Date | Opens a calendar dropdown to select a precise date/time cutoff. Ignores files captured before this moment. | Selective backups (e.g. importing photos taken only during this summer's trip). |
Attribution & Curation Options
- Contributor Profiles: Attribute imported photos to a specific profile (e.g. "Clara's Camera", "Dave's iPhone"). This adds search-friendly owner metadata to the photo catalog.
- Curation Staging Queue: Check "Stage in Curation Queue before import" to hold new photos in a temporary database table. You can open the Curation Workspace later to approve, rename, edit details, or discard items before committing them to the main library.
Target File Organization
Photos are copied into the Photos Master Directory (configured in Settings → Sources) and organized in a clean, chronological folder hierarchy:
Photos Master Directory/
└── YEAR/
└── YEAR_MONTH_DAY/
└── original_filename.ext
If a file with an identical name already exists in the destination folder, the importer automatically resolves the conflict by appending a counter (e.g., photo_1.jpg, photo_2.jpg) instead of overwriting the file.
3 Background Ingestion Architecture
To ensure that the user interface never blocks or freezes during heavy file operations, imports run inside a separate, fully detached background process:
Execution Flow
- When you click Start Import, the main GUI serializes all parameters (source path, date filters, profile attributes) into a file named
importer_job.jsonwithin~/.AegisPhotoVoyager/. - Any old status tracking files (e.g.
import_status.json) are deleted. - The GUI spawns a detached subprocess:
# Command executed internally by Pyside6 QProcesspython main.py --mode importer - The background process begins reading
importer_job.json, scanning files, and running copy operations. Because it is completely detached, you can safely close the main application and the import will continue running in the background until completion.
4 Status Monitoring & Liveness
During the import, the detached process periodically writes stats to import_status.json. The main GUI reads this file to update the progress bar, activity label, and ETA calculations.
Status File Payload Structure
The JSON status file follows a strict schema, reporting overall progress as well as statistics on duplicates and items copied:
{
"name": "importer",
"pid": 24890,
"current_status": "running",
"current_activity": "Importing DSC_0024.JPG",
"progress_pct": 34,
"start_time": 1784382900.5,
"details": {
"scanned": 120,
"duplicates": 15,
"imported": 40,
"total": 350
},
"updated_at": "2026-07-18T14:08:30.123456"
}
Liveness & ETA Algorithms
- Liveness Verification: The GUI uses a polling loop (every 500ms in progress dialogs; 1000ms on the main window) that queries the
pidreported in the status JSON. It verifies the process is active usingpsutil.pid_exists(pid), with a fallback to native signal checking (os.kill(pid, 0)) to avoid reporting "stuck" progress if a background worker unexpectedly crashes. - Dynamic ETA: Remaining time is computed on-the-fly:
Rate = Processed Files / Elapsed Time
ETA (seconds) = (Total Files - Processed Files) / Rate - Job Cancellation: If you click Cancel, the GUI pulls the background PID from the status file and terminates the task immediately by sending a termination signal (
SIGTERM/SIGKILL). - Metrics Logging: Upon completion or error, the worker updates the status file to
finishedorerror, saves metadata stats into the local SQLite tableactivity_metrics, and logs details inbackground_history.jsonl.
5 Starting AI Processing
After your photos have been successfully copied into the master directory, the local AI processing pipeline is triggered to index and enrich the new assets.
Automated Post-Import Sequence
If you did not use the Curation Staging Queue (photos were copied directly into the master library):
- The importer immediately launches the File Discovery (
discoverer) background process to register the files in the catalog. - Once registered, the GUI's sequence timer kicks off the sequential processing queue.
The Sequential AI State Machine
To avoid resource contention (e.g. running out of VRAM/RAM or overheating), the pipeline executes stages one by one. If you close the app during this sequence, background workers continue running, and the GUI restores polling hooks next time it starts.
| Stage | Worker Mode | Process Details |
|---|---|---|
| 1. Discovery | discoverer |
Scans folders recursively and registers raw files with EXIF metadata in SQLite. |
| 2. Image Processing | processor |
Generates 300px grid thumbnails, 2048px preview proxies, and computes the perceptual hash (pHash). |
| 3. AI Metadata Analysis | ai_analyzer |
Sends image frames to your local vision LLM (Ollama/LM Studio) to extract descriptions, tags, mood, and objects. |
| 4. Technical Quality | quality_evaluator |
Evaluates sharpness, focus, grain, white balance, contrast, and composition (0-100 score). |
| 5. Semantic Prep | semantic_indexer |
Generates text embeddings from the LLM descriptions and saves them in the vector database for natural language search queries. |
| 6. Visual Similarity Map | clip_embedder |
Computes CLIP embeddings for 3D coordinate clustering in the visual similarity map. |
| 7. Face Recognition | face_recognizer |
Detects facial bounding boxes and groups matching faces using DBSCAN clustering. |
Pre-Flight Validation & Smart Start
Before launching the pipeline manually (via the ▶ play button on the toolbar or the Smart Start onboarding wizard), the application runs several checks to make sure your AI engines are ready:
- LLM Connection Check: Sends a test image request to Ollama / LM Studio to verify it yields valid structured JSON.
- Face Recognition Diagnostic: Validates that required dependencies like
face_recognitionand model files are present on disk. - Embedding Model Test: Assures the text embedding model (e.g.
nomic-embed-text) is loaded and responding with numerical vector arrays.
Troubleshooting
| Symptom | Potential Cause | Solution / Steps to Resolve |
|---|---|---|
| iPhone / USB device fails to connect | Device locked or host mount locked by a system process. | Unlock your phone, tap "Trust this Computer", input your passcode, and click the retry button. On Linux, if it is stuck, run idevicepair pair in a terminal. |
| ZIP extraction is very slow or gets stuck | Slow storage drive or extremely large file archive size. | Ensure your temporary directory is located on a fast SSD. Check the available space under ~/.AegisPhotoVoyager/logs/importer.log. |
| Duplicate photos are being copied | Perceptual hash collision or disabled hash verification. | Verify that the pHash threshold is configured correctly under Settings → Library. If duplicates have minor edits (like different filters), they may bypass low-sensitivity settings. |
| Progress bar is frozen at 0% or stays "stuck" | Background process crashed or was blocked by OS sandbox/permissions. | Check the status check log. Make sure the background process was launched and look for system level blocks (e.g. strict Snap/Flatpak sandboxes that restrict filesystem or process signals). |