A macOS menu bar app that displays videos as animated desktop wallpaper. Sister project to Video Screen Saver.
- SwiftUI: Menu bar interface (MenuBarExtra), tabbed settings window
- AppKit: Desktop-level window management (NSWindow at desktop level)
- AVFoundation: Video playback (AVPlayer, AVPlayerLayer, dual-player transitions)
- Deployment Target: macOS 13.0+
- Sandbox: Enabled with file access entitlements
VideoWallpaper/
├── App/
│ ├── VideoWallpaperApp.swift # @main, MenuBarExtra scene
│ └── AppDelegate.swift # Window management, playback coordination
├── Core/
│ ├── VideoPlayerManager.swift # AVPlayer dual-player system, transitions
│ ├── FolderBookmarkManager.swift # Security-scoped bookmarks
│ ├── PlaylistManager.swift # Shuffle, loop, video discovery
│ ├── PlaylistPersistence.swift # Per-monitor playlist storage
│ ├── SyncManager.swift # Sync mode coordination
│ └── VideoMetadataLoader.swift # Async metadata extraction
├── Desktop/
│ ├── DesktopWindowController.swift # NSWindow at desktop level
│ ├── DesktopVideoView.swift # NSView hosting AVPlayerLayer
│ └── MultiMonitorManager.swift # Per-screen window management
├── UI/
│ ├── StatusMenuView.swift # Menu bar dropdown content
│ ├── MainTabView.swift # 5-tab container
│ ├── StatusTab.swift # Status/controls
│ ├── PlaylistTab.swift # Video list editor
│ └── SettingsView.swift # Display/Advanced tabs
└── Utilities/
├── PowerManager.swift # Battery state monitoring
└── LaunchAtLoginManager.swift # SMAppService wrapper
SyncManager.shared- Controls sync mode, owns shared VideoPlayerManager when sync enabledPlaylistPersistence.forScreen(screenId)- Per-monitor playlist storageAppDelegate.shared- Manages desktop windows, coordinates playback
let desktopLevel = CGWindowLevelForKey(.desktopWindow)
window.level = NSWindow.Level(rawValue: Int(desktopLevel) - 1)- Independent mode: Each DesktopWindowController owns its VideoPlayerManager
- Sync mode: All screens share ONE VideoPlayerManager via SyncManager
Each screen creates its OWN AVPlayerLayer instances (CALayer can only have one superlayer). They point to shared AVPlayer instances for playback.
playlist_<screenId>_items- JSON-encoded playlistplaylist_<screenId>_shuffle- Boolplaylist_<screenId>_loop- Bool
playlist_global_metadata- Video metadata cachesyncDisplays- BoolvideoFoldersBookmarks- Security-scoped folder bookmarkspauseOnBattery,launchAtLogin,transitionType,transitionDuration,videoScaling
Full project documentation in docs/ (Directions system):
docs/PROJECT_STATE.md- Current phase and focusdocs/decisions.md- Architecture decision logold-docs/- Previous session logs and activity history