# MovieMapper iOS Implementation Plan ## Architecture Overview Since you want **SwiftUI with Swift only** (no Rust backend) and **offline-first**, this will be a complete rewrite in Swift using Apple frameworks. --- ## Phase 1: Project Setup & Foundation ### 1.1 Create Xcode Project ```bash # Create new iOS App project (iPad only) xcodebuild -createProject MovieMapper -template iOS-App -destination ./MovieMapper-iOS # Or using Swift Package Manager for more control mkdir MovieMapper-iOS && cd MovieMapper-iOS swift package init --type executable --name MovieMapper-iOS ``` ### 1.2 Directory Structure ``` MovieMapper-iOS/ ├── MovieMapper-iOS/ # Main app target │ ├── ContentView.swift │ ├── MovieMapperApp.swift │ └── ... ├── SharedModels/ # Shared data models │ ├── MediaFile.swift │ ├── Show.swift │ ├── Season.swift │ ├── Episode.swift │ └── TaggedFile.swift ├── Services/ # Business logic │ ├── FileScanner.swift │ ├── MetadataExtractor.swift │ ├── TVDBClient.swift │ ├── FileMapper.swift │ └── AuditLogger.swift ├── UI/ # SwiftUI views │ ├── BrowseView.swift │ ├── SearchView.swift │ ├── FileListView.swift │ ├── SeasonDetailView.swift │ └── ModalViews/ ├── Utils/ # Helper functions │ ├── Filesystem.swift │ ├── FFmpegWrapper.swift │ └── DateFormatters.swift └── Resources/ # Assets ├── Assets.xcassets └── Localizable.strings ``` --- ## Phase 2: Core Implementation ### 2.1 Data Models (SharedModels/) ```swift struct MediaFile: Identifiable, Codable, Hashable { let id: UUID let name: String let path: String let size: Int64 let modified: Date let duration: String // "mm:ss" format let quality: String // "1080p", "4K", etc. let fps: String // "24fps", "30fps", etc. var isFolder: Bool var tags: [TagType] } enum TagType: String, Codable, CaseIterable { case extra = "extra" case behindTheScenes = "behind-the-scenes" case delete = "delete" } ``` ### 2.2 File Scanner Service - **Framework**: Use `FileManager` with `URLQueryItem` for directory access - **Permissions**: Request `NSPhotoLibraryAddUsageDescription` for file access - **Non-recursive scanning**: Scan only current directory (match desktop behavior) - **Progress updates**: Use Combine/Publishers for real-time UI updates ```swift class FileScanner { func scanDirectory(at path: URL, progress: @escaping (Int, Int, String) -> Void) async throws -> [MediaFile] } ``` ### 2.3 Metadata Extractor - **FFmpeg integration**: Use `ffmpeg-kit` or `SwiftFFmpeg` package - **Extract**: duration, resolution, FPS - **Error handling**: Gracefully handle corrupted files ```swift class MetadataExtractor { func extractDuration(from url: URL) async throws -> String func extractQuality(from url: URL) async throws -> (quality: String, fps: String) } ``` ### 2.4 TVDB Client - **API**: TheTVDB v4 REST API - **Authentication**: Bearer token with caching - **Features**: Search shows, get details, fetch episodes - **Offline caching**: Store recent searches in `UserDefaults` or Core Data ```swift class TVDBClient { func authenticate() async throws func search(query: String) async throws -> [Show] func getShowDetails(id: Int) async throws -> ShowDetails func getSeasonEpisodes(showId: Int, seasonNumber: Int) async throws -> [Episode] } ``` ### 2.5 File Mapper - **Jellyfin naming**: `ShowName S01E01 - quality.ext` - **Episode ranges**: Support `S01E01-E03` format - **Folder creation**: Create `extras/`, `behind the scenes/`, `commentary/` directories ```swift class FileMapper { func mapFiles(_ files: [MediaFile], to show: Show, season: Season) async throws -> MappingResult } ``` ### 2.6 Audit Logger - **Format**: JSON lines in `.audit` files - **Storage**: Write to same directory as files - **Content**: Timestamp, action, details ```swift class AuditLogger { func log(action: String, details: [String: Any], in directory: URL) async throws } ``` --- ## Phase 3: UI Implementation ### 3.1 Main Views #### BrowseView.swift - Directory picker (UIDocumentPickerViewController) - Breadcrumb navigation (iPad multi-column) - File list with: - Folder icons 📁 - Media file details (duration, quality, FPS) - Tag icons (clickable) - Play button (moves tagged files) #### SearchView.swift - Search bar for TVDB - Show results with thumbnails - Season selection with episode lists - Episode count badges #### FileListView.swift - Drag-and-drop reordering (iOS 17+) - Episode number editing - Tag toggling (extra, behind-the-scenes, delete) - Floating action button for moving all tagged files ### 3.2 iPad-Specific Features - **Multi-column navigation**: Use `NavigationSplitView` for sidebar + content - **Split view**: Search on left, files on right - **Large screen optimization**: Use more horizontal space ### 3.3 Modal Views - Video preview modal - Confirmation dialogs for destructive actions - Loading indicators for async operations --- ## Phase 4: Testing Strategy ### 4.1 Command Line Testing Tools #### 4.1.1 File Scanner Tests ```bash # Create test directory structure mkdir -p /tmp/moviemapper_test/{Season\ 01,Season\ 02} touch /tmp/moviemapper_test/Season\ 01/episode1.mp4 touch /tmp/moviemapper_test/Season\ 01/episode2.mkv # Run scanner test swift test --filter FileScannerTests/testScanDirectory ``` #### 4.1.2 Metadata Extraction Tests ```bash # Create test video file (using ffmpeg) ffmpeg -f lavfi -i testsrc=duration=5:size=1920x1080:rate=30 /tmp/test_video.mp4 # Run metadata extraction test swift test --filter MetadataExtractorTests/testExtractDuration swift test --filter MetadataExtractorTests/testExtractQuality ``` #### 4.1.3 TVDB API Tests ```bash # Set API key (from .env file) export TVDB_API_KEY="your-api-key-here" # Run TVDB integration tests swift test --filter TVDBClientTests/testAuthenticate swift test --filter TVDBClientTests/testSearchShows swift test --filter TVDBClientTests/testGetShowDetails ``` #### 4.1.4 File Mapping Tests ```bash # Create test files mkdir -p /tmp/moviemapper_test/Season\ 01 for i in {1..5}; do ffmpeg -f lavfi -i testsrc=duration=1:size=1280x720:rate=24 \ /tmp/moviemapper_test/Season\ 01/video_${i}.mp4 done # Run mapping test swift test --filter FileMapperTests/testMapSingleEpisode swift test --filter FileMapperTests/testMapEpisodeRange ``` ### 4.2 UI Testing ```bash # Run UI tests xcodebuild test -project MovieMapper-iOS.xcodeproj \ -scheme MovieMapper-iOS \ -destination 'platform=iOS Simulator,name=iPad Pro (12.9-inch) (17th generation)' \ -destination-timeout 60 ``` ### 4.3 Performance Testing ```bash # Test scanning performance with many files xcodebuild test -project MovieMapper-iOS.xcodeproj \ -scheme MovieMapper-iOSPerformance \ -destination 'platform=iOS Simulator,name=iPad Pro' \ -enableCodeCoverage YES ``` --- ## Phase 5: Advanced Features ### 5.1 Offline-First Architecture - **Local caching**: Store show search results in `UserDefaults` or `CoreData` - **File scanning**: Work completely offline - **TVDB features**: Optional, only when internet available ### 5.2 File System Access - **Document Picker**: Let user select media library location - **Security scoped bookmarks**: Persist access across app launches - **iCloud integration**: Optional (for backup) ### 5.3 Tagging System ```swift struct TagManager { func addTag(_ tag: TagType, to file: MediaFile, in directory: URL) async throws func removeTag(_ tag: TagType, from file: MediaFile, in directory: URL) async throws func moveTaggedFiles(_ files: [MediaFile], to folder: String) async throws } ``` --- ## Phase 6: Build & Distribution ### 6.1 Build Commands ```bash # Build for simulator xcodebuild -project MovieMapper-iOS.xcodeproj \ -scheme MovieMapper-iOS \ -configuration Debug \ -sdk iphonesimulator # Build for device xcodebuild -project MovieMapper-iOS.xcodeproj \ -scheme MovieMapper-iOS \ -configuration Release \ -sdk iphoneos \ -archivePath MovieMapper-iOS.xcarchive \ archive # Export IPA xcodebuild -exportArchive \ -archivePath MovieMapper-iOS.xcarchive \ -exportOptionsPlist ExportOptions.plist ``` ### 6.2 Testing Automation ```bash #!/bin/bash # run-tests.sh # Set up test environment export TVDB_API_KEY=$(cat .env | grep TVDB_API_KEY | cut -d'=' -f2) # Run unit tests swift test # Run UI tests xcodebuild test -project MovieMapper-iOS.xcodeproj \ -scheme MovieMapper-iOS \ -destination 'platform=iOS Simulator,name=iPad Pro' # Generate test report xcodebuild -project MovieMapper-iOS.xcodeproj \ -scheme MovieMapper-iOS \ -destination 'platform=iOS Simulator,name=iPad Pro' \ -quiet \ -testSummaryReport /tmp/test-results.xml ``` --- ## Phase 7: Dependencies & Packages ### 7.1 Swift Package Manager Dependencies ```swift // swift-tools-version:5.9 import PackageDescription let package = Package( name: "MovieMapper-iOS", platforms: [.iOS(.v17)], dependencies: [ .package(url: "https://github.com/ffmpeg-kit/swift.git", from: "6.0.0"), .package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.8.0"), .package(url: "https://github.com/rnapier/ReactiveSwift.git", from: "7.0.0") ], targets: [ .target( name: "MovieMapper-iOS", dependencies: [ .product(name: "FFmpegKitSwift", package: "swift-ffmpeg-kit"), .product(name: "Alamofire", package: "Alamofire"), .product(name: "ReactiveSwift", package: "ReactiveSwift") ] ) ] ) ``` --- ## Summary of Key Decisions ### Architecture - **Pure SwiftUI** with Swift only (no Rust backend as requested) - **Combine** for async operations and state management - **Structured concurrency** (`async/await`) for all network/file operations ### Offline-First Design - File scanning works completely offline - TVDB features are optional (require internet) - Local caching of recent searches and show details ### iPad-Specific Optimizations - Multi-column navigation with `NavigationSplitView` - Large screen layout with sidebar + content - Touch-friendly UI elements (minimum 44pt tap targets) ### Testing Approach - **Command line tests**: Unit tests for all services - **UI tests**: Simulator-based automation - **Manual testing**: Test on actual iPad devices --- ## Implementation Timeline | Week | Task | |------|------| | 1-2 | Project setup, data models, basic file scanner | | 3-4 | Metadata extractor, UI scaffolding | | 5-6 | TVDB client, search functionality | | 7-8 | File mapping, tagging system | | 9 | Testing (unit + UI), bug fixes | | 10 | iPad optimization, polishing | --- ## Next Steps 1. **Create Xcode project** using the structure above 2. **Implement core data models** (MediaFile, Show, Season, Episode) 3. **Build FileScanner service** with progress reporting 4. **Create SwiftUI views** for directory browsing 5. **Implement metadata extraction** with FFmpeg 6. **Add TVDB integration** for show search 7. **Build tagging and file movement** features 8. **Write comprehensive tests** for all services 9. **Optimize for iPad** with split views and large screen layouts 10. **Test on physical iPad device** before App Store submission