Skip to content

Latest commit

Β 

History

95 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

DJI Avata Workflow Automator

Dual-platform (macOS/iOS) media workflow automation app built with Swift/SwiftUI, featuring USB device detection, GPS metadata extraction, intelligent geocoding with persistent caching, automated file renaming, and cloud synchronization with CloudKit + MEGA integration.

Tech Stack

Languages: Swift, XML, C (SQLite3 bindings)

Platforms: macOS 26.0+, iOS 17.0+

UI: SwiftUI (primary), AppKit/UIKit (platform-specific), MapKit (iOS)

Data: Core Data + CloudKit sync, SQLite3 (geocoding cache), UserDefaults

Networking: Foundation URLSession, Network framework (Bonjour TCP server), Mapbox API, MEGA API

System: IOKit (USB detection), AVFoundation/AVKit (video), CoreLocation (geocoding), CryptoKit/CommonCrypto (encryption), Security (Keychain)

Concurrency: async/await, Combine, GCD, OperationQueue

External Tools: ExifTool (metadata), Gyroflow (stabilization)

Architecture: MVVM + Coordinator, Protocol-oriented design, Dependency injection

🎯 Project Status

Current Phase: Metadata Extraction & Smart Renaming (80% Complete)

βœ… Completed Features

  • USB device detection for DJI Avata (detects "Untitled" volume, 45-50GB)
  • File scanning from drone storage (/DCIM/DJI_001/)
  • Preview tab with video thumbnails and playback
  • Safe mode to prevent processing existing files
  • Individual workflow step execution for debugging
  • Permission handling for external volumes
  • GPS extraction from SRT telemetry files
  • Multi-provider geocoding (Mapbox β†’ CoreLocation β†’ ExifTool β†’ Coordinates)
  • Geocoding cache system (persistent, ~1km radius matching)
  • Smart file renaming UI with suggestions
  • Apply individual or batch rename operations
  • Debug tab with file management

🚧 In Progress / Known Issues

  • ExifTool path detection: Currently fixing initialization error for detecting ExifTool location
    • Error: exifToolPath initialization issue in MetadataExtractor.swift
    • ExifTool is installed at /opt/homebrew/bin/exiftool
    • Need to complete the path detection logic properly

πŸ“‹ TODO / Not Yet Implemented

  • Video stabilization with Gyroflow
  • MEGA cloud sync integration
  • File cleanup after successful sync
  • Settings persistence for workflow preferences

πŸ—οΈ Architecture

Design Pattern

MVVM + Coordinator architecture for clear separation of concerns.

Key Components

Core Layer (/Workflow/Core/)

  • WorkflowManager.swift: Central orchestrator for all workflow steps

    • Manages workflow state and progress
    • Coordinates between different services
    • Tracks processed files and their metadata
  • USBDetector.swift: Detects DJI Avata connection

    • Uses IOKit and NSWorkspace
    • Identifies "Untitled" volume with 45-50GB capacity
    • Publishes connection status via Combine
  • MetadataExtractor.swift: GPS and geocoding functionality

    • Extracts GPS from SRT telemetry files (multiple format support)
    • Geocoding cascade: Mapbox β†’ CoreLocation β†’ ExifTool β†’ Coordinates
    • Persistent cache: Saves geocoded locations to UserDefaults
    • Cache matching: ~1km radius for nearby coordinates
    • Embeds metadata back into video files
  • FileManager.swift: Scans and organizes drone files

    • Detects file types (MP4, JPG, SRT, LRV)
    • Categorizes as video, photo, telemetry, or preview

UI Layer (/Workflow/UI/)

  • ContentView.swift: Main app container with tab navigation
  • PreviewView.swift: File preview with thumbnails and video player
  • WorkflowView.swift: Individual step execution with logs
  • DebugView.swift: Smart renaming interface
    • Shows suggested names after metadata extraction
    • Individual "Apply" buttons per file
    • "Apply All Suggestions" for batch operations
    • Progress banners and visual feedback
  • SettingsView.swift: Configuration interface
    • Storage mode selection (Local/Cloud)
    • Workflow options (safe mode, GPU acceleration)
    • Mapbox token configuration
    • Tool status checking

πŸ”§ Technical Details

GPS Extraction from SRT Files

The app supports multiple DJI SRT telemetry formats:

// Format 1: GPS(lat,lon,alt)
GPS(37.7749, -122.4194, 15.5)

// Format 2: latitude: XX longitude: YY
latitude: 37.7749 longitude: -122.4194

// Format 3: DJI extended format
[GPS: (37.7749, -122.4194, 15.5)]

Uses median filtering to handle GPS outliers from multiple telemetry points.

Geocoding System

Multi-tier fallback ensures location resolution:

1. Check Cache (~1km radius)
   ↓ (cache miss)
2. Try Mapbox API (if token configured)
   ↓ (fails or no token)
3. Try Apple CoreLocation
   ↓ (fails)
4. Try ExifTool geolocation API
   ↓ (fails)
5. Use coordinates as filename (Lat37.7749_Lon-122.4194)

Cache Implementation:

  • Key: "latitude,longitude" string
  • Stored in UserDefaults under "GeocodingCache"
  • Loaded on MetadataExtractor init
  • Proximity matching prevents duplicate API calls for nearby locations

Smart File Naming

Format: City-State-Timestamp.ext

Examples:

  • San_Francisco-California-20241029_140523.MP4
  • Brooklyn-New_York-20241028_163045.MP4
  • Lat37.7749_Lon-122.4194-20241029_120000.MP4 (coordinate fallback)

Sanitization: Replaces spaces, slashes, and backslashes with underscores to ensure filesystem compatibility.

Permission Handling

Entitlements (Workflow.entitlements):

<!-- Sandbox disabled for development -->
<key>com.apple.security.app-sandbox</key>
<false/>

<!-- File access -->
<key>com.apple.security.files.user-selected.read-write</key>
<true/>

<!-- Removable volumes (USB drives) -->
<key>com.apple.security.files.removable-volume.read-write</key>
<true/>

<!-- Network for geocoding and MEGA -->
<key>com.apple.security.network.client</key>
<true/>

<!-- Run subprocesses (ExifTool, Gyroflow) -->
<key>com.apple.security.inherit</key>
<true/>

πŸ› Known Issues & Debugging Context

Issue 1: ExifTool Path Detection Error

Location: Workflow/Core/MetadataExtractor.swift:94

Error Messages:

Return from initializer without initializing all stored properties
Immutable value 'self.exifToolPath' may only be initialized once

Context:

  • ExifTool is installed at /opt/homebrew/bin/exiftool (symlink to /opt/homebrew/Cellar/exiftool/13.36/bin/exiftool)
  • The init() method tries to find ExifTool in multiple locations
  • Changed exifToolPath from let to var to allow reassignment
  • Need to initialize it once before the search logic

Current Code Structure:

class MetadataExtractor {
    private var exifToolPath: String  // Changed from 'let' to 'var'

    init() {
        // Initialize with default first
        self.exifToolPath = "/usr/local/bin/exiftool"

        // Then search for actual path
        let possiblePaths = [
            "/opt/homebrew/bin/exiftool",
            "/opt/homebrew/Cellar/exiftool/13.36/bin/exiftool",
            "/usr/local/bin/exiftool",
            "/usr/bin/exiftool",
            "/opt/local/bin/exiftool"
        ]

        // Try to find and update exifToolPath...
    }
}

What Was Attempted:

  1. Added symlink resolution logic
  2. Added direct Cellar path checking
  3. Added 'which' command fallback
  4. Enhanced logging for debugging

Next Steps to Fix:

  • The user modified line 7 to make exifToolPath a var instead of let
  • The initialization at line 21 sets the default
  • The error suggests there's still a code path where it's not initialized
  • Check if all code paths properly initialize or update exifToolPath

Issue 2: Geocoding Returns "Unknown"

Status: βœ… FIXED

Solution Implemented:

  • Added persistent caching system
  • Improved Mapbox API parsing (added types filter)
  • Enhanced CoreLocation fallback with multiple field checks
  • Added coordinate-based fallback naming
  • Better error logging with emojis for debugging

πŸ“¦ Dependencies

External Tools

  • ExifTool: Metadata extraction and embedding

    • Install: brew install exiftool
    • Expected locations: /opt/homebrew/bin/ or /usr/local/bin/
  • Gyroflow (planned): Video stabilization

    • Not yet integrated

Frameworks

  • SwiftUI: UI framework
  • Combine: Reactive programming for USB detection
  • AVKit: Video playback and thumbnail generation
  • CoreLocation: Geocoding fallback
  • IOKit: USB device detection

APIs

  • Mapbox Geocoding API (optional): Enhanced location resolution
    • Token stored in UserDefaults: "MapboxAccessToken"
    • Configure in Settings β†’ Geocoding tab
    • Format: pk.xxxxx... (~80-100 chars)

πŸš€ How to Use

Setup

  1. Install ExifTool: brew install exiftool
  2. (Optional) Get Mapbox token from https://www.mapbox.com/signup
  3. Build and run the app in Xcode

Workflow

  1. Connect Drone: Plug in DJI Avata USB drive

    • App detects "Untitled" volume automatically
  2. Preview Files: Switch to Preview tab

    • See thumbnails of all videos and photos
    • Double-click to open in Finder
    • Click thumbnail to play video in modal
  3. Extract Metadata: Go to Debug tab

    • Click "Extract All Metadata" button
    • Watch progress: "Scanning β†’ Extracting GPS β†’ Generating names"
    • Blue banner appears with suggestions
  4. Review Suggestions: Each file shows suggested name

    • Format: City-State-Timestamp.ext
    • Green "Apply" button per file
    • Or use "Apply All Suggestions" for batch
  5. Apply Renames: Click Apply button(s)

    • Individual confirmation for single files
    • Batch rename without individual confirmations
    • Success/failure logged to console

Settings

General Tab:

  • Storage Mode: Local folder or MEGA cloud
  • Auto-start workflow on USB connection

Workflow Tab:

  • Safe Mode: Skip already-processed files
  • Delete LRV preview files
  • Delete after successful sync
  • GPU acceleration for stabilization
  • Parallel renders (1-8 simultaneous)

Geocoding Tab:

  • Mapbox Access Token configuration
  • Token validation and testing
  • Network connection test (may fail in sandbox)

Tools Tab:

  • Check installed tool status
  • Quick install commands

πŸ“‚ Project Structure

Workflow/
β”œβ”€β”€ Workflow.xcodeproj
β”œβ”€β”€ Workflow/
β”‚   β”œβ”€β”€ WorkflowApp.swift           # App entry point
β”‚   β”œβ”€β”€ Core/
β”‚   β”‚   β”œβ”€β”€ WorkflowManager.swift   # Main coordinator
β”‚   β”‚   β”œβ”€β”€ USBDetector.swift       # Device detection
β”‚   β”‚   β”œβ”€β”€ MetadataExtractor.swift # GPS & geocoding ⚠️
β”‚   β”‚   β”œβ”€β”€ FileManager.swift       # File operations
β”‚   β”‚   β”œβ”€β”€ LogManager.swift        # Logging utility
β”‚   β”‚   └── Protocols/              # Protocol definitions
β”‚   β”œβ”€β”€ UI/
β”‚   β”‚   β”œβ”€β”€ ContentView.swift       # Main container
β”‚   β”‚   β”œβ”€β”€ PreviewView.swift       # File previews
β”‚   β”‚   β”œβ”€β”€ WorkflowView.swift      # Step execution
β”‚   β”‚   β”œβ”€β”€ DebugView.swift         # Smart renaming
β”‚   β”‚   └── SettingsView.swift      # Configuration
β”‚   β”œβ”€β”€ Models/
β”‚   β”‚   └── (Data models)
β”‚   └── Workflow.entitlements       # App permissions
β”œβ”€β”€ README.md                        # This file
└── DJIAvataWorkflow/               # Old folder (can be ignored)

πŸ” Debugging Tips

View Logs

All operations log to console with emojis for easy scanning:

  • πŸ“ Geocoding operations
  • βœ… Successful operations
  • ⚠️ Warnings
  • ❌ Errors
  • πŸ’Ύ Cache operations
  • πŸ—ΊοΈ Mapbox API calls
  • 🍎 CoreLocation fallbacks

Check ExifTool

# Verify installation
which exiftool
# Should output: /opt/homebrew/bin/exiftool

# Check if it's working
exiftool -ver
# Should output: 13.36 (or similar)

# Test GPS extraction from SRT
exiftool path/to/file.SRT | grep GPS

Test Geocoding

Check Mapbox Token:

# In Settings β†’ Geocoding, click "Test Connection"
# Watch logs for HTTP status and response

Check Cache:

# Cache is stored in UserDefaults
defaults read com.yourorg.Workflow GeocodingCache

USB Detection Issues

# List connected volumes
diskutil list

# Should see "Untitled" volume around 49.5 GB
# Example: /dev/disk4 (external, physical)

πŸŽ“ Development Notes

Compilation Issues Encountered

  1. Escaped backslashes: Changed \\( to \( throughout codebase
  2. WorkflowError conformance: Changed to WorkflowErrorType.noDevice
  3. CFHost type mismatch: Changed Bool to DarwinBoolean for sandbox checks
  4. Force unwrap errors: Fixed CLPlacemark unwrapping in CoreLocation fallback
  5. ExifTool initialization: Current issue - see "Known Issues" above

Testing Without Drone

The app requires actual DJI Avata hardware for full testing. For development:

  • Mock the USB detector to return a test device
  • Place sample MP4/SRT files in a test directory
  • Update FileManager to scan test directory instead

Sandbox Considerations

Sandbox is currently disabled for development (app-sandbox = false). Before production:

  1. Enable sandbox
  2. Test all file operations with security-scoped bookmarks
  3. Verify network access for Mapbox
  4. Test ExifTool execution in sandboxed environment

πŸ“ Session Continuation Checklist

When picking up this project in a new session:

  1. First, fix the ExifTool initialization error:

    • File: Workflow/Core/MetadataExtractor.swift
    • Issue: Property initialization in init()
    • Goal: Properly detect ExifTool at /opt/homebrew/bin/exiftool
  2. Test the full metadata extraction flow:

    • Connect drone (or use test files)
    • Click "Extract All Metadata"
    • Verify GPS extraction from SRT files
    • Verify geocoding (check logs for Mapbox/CoreLocation)
    • Verify cache is working (second run should be faster)
    • Verify suggested names appear correctly
  3. Next features to implement (in priority order):

    • Fix any remaining geocoding edge cases
    • Implement video stabilization with Gyroflow
    • Implement MEGA sync functionality
    • Implement cleanup after successful sync
    • Add progress indicators for long operations
  4. Known limitations to address:

    • Hardcoded ExifTool version in Cellar path (13.36)
    • No retry logic for failed API calls
    • No rate limiting for Mapbox API
    • Cache never expires (consider TTL)
    • No bulk metadata embedding (processes files one by one)

πŸ’‘ Quick Reference

Important File Paths

  • Drone storage: /Volumes/Untitled/DCIM/DJI_001/
  • ExifTool: /opt/homebrew/bin/exiftool
  • App logs: Check Xcode console
  • Geocoding cache: UserDefaults key "GeocodingCache"

Key AppStorage Keys

"storageMode"           // "Local" or "Cloud (MEGA)"
"localSyncFolder"       // Path to local sync directory
"megaSyncFolder"        // Path to MEGA sync directory
"autoStartWorkflow"     // Boolean
"safeMode"              // Boolean
"deleteLRVFiles"        // Boolean
"deleteAfterSync"       // Boolean
"useGPUAcceleration"    // Boolean
"parallelRenders"       // Int (1-8)
"MapboxAccessToken"     // String (optional)

File Type Detection

.MP4, .MOV  β†’ .video(codec: "h265")
.JPG, .JPEG β†’ .photo
.SRT        β†’ .telemetry
.LRV        β†’ .preview (low-res preview files)

Mapbox API Endpoint

GET https://api.mapbox.com/geocoding/v5/mapbox.places/{lon},{lat}.json
?access_token={token}
&types=place,locality,district

πŸ™ Credits

Built with SwiftUI for macOS 14.0+ Uses ExifTool by Phil Harvey Geocoding powered by Mapbox (optional)


Last Updated: 2024-10-29 Version: 0.8.0-alpha Status: Active Development

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages