|
|
|
|
@ -7,6 +7,7 @@ struct MusicApp: App { |
|
|
|
|
@State private var playerVM: PlayerViewModel? |
|
|
|
|
@State private var scannerService: ScannerService? |
|
|
|
|
@State private var audioService = AudioService() |
|
|
|
|
@State private var playlistVM: PlaylistViewModel? |
|
|
|
|
@State private var initError: String? |
|
|
|
|
|
|
|
|
|
var body: some Scene { |
|
|
|
|
@ -15,12 +16,14 @@ struct MusicApp: App { |
|
|
|
|
if let db = dbService, |
|
|
|
|
let library = libraryVM, |
|
|
|
|
let player = playerVM, |
|
|
|
|
let scanner = scannerService { |
|
|
|
|
let scanner = scannerService, |
|
|
|
|
let playlist = playlistVM { |
|
|
|
|
ContentView( |
|
|
|
|
library: library, |
|
|
|
|
player: player, |
|
|
|
|
scanner: scanner, |
|
|
|
|
audio: audioService |
|
|
|
|
audio: audioService, |
|
|
|
|
playlist: playlist |
|
|
|
|
) |
|
|
|
|
} else if let error = initError { |
|
|
|
|
Text("Failed to initialize database: \(error)") |
|
|
|
|
@ -54,17 +57,22 @@ struct MusicApp: App { |
|
|
|
|
let scanner = ScannerService(db: db) |
|
|
|
|
let library = LibraryViewModel(db: db) |
|
|
|
|
let player = PlayerViewModel(audio: audioService, db: db) |
|
|
|
|
let playlist = PlaylistViewModel(db: db) |
|
|
|
|
|
|
|
|
|
self.dbService = db |
|
|
|
|
self.scannerService = scanner |
|
|
|
|
self.libraryVM = library |
|
|
|
|
self.playerVM = player |
|
|
|
|
self.playlistVM = playlist |
|
|
|
|
|
|
|
|
|
if let savedFolder = UserDefaults.standard.string(forKey: "musicFolderPath"), |
|
|
|
|
let url = URL(string: savedFolder) { |
|
|
|
|
if let url = resolveBookmark() { |
|
|
|
|
Task { |
|
|
|
|
await scanner.rescan(url) |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
DispatchQueue.main.async { |
|
|
|
|
pickFolder() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} catch { |
|
|
|
|
initError = error.localizedDescription |
|
|
|
|
@ -80,7 +88,7 @@ struct MusicApp: App { |
|
|
|
|
|
|
|
|
|
guard panel.runModal() == .OK, let url = panel.url else { return } |
|
|
|
|
|
|
|
|
|
UserDefaults.standard.set(url.absoluteString, forKey: "musicFolderPath") |
|
|
|
|
saveBookmark(for: url) |
|
|
|
|
|
|
|
|
|
if let scanner = scannerService { |
|
|
|
|
Task { |
|
|
|
|
@ -88,4 +96,39 @@ struct MusicApp: App { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private func saveBookmark(for url: URL) { |
|
|
|
|
do { |
|
|
|
|
let data = try url.bookmarkData( |
|
|
|
|
options: .withSecurityScope, |
|
|
|
|
includingResourceValuesForKeys: nil, |
|
|
|
|
relativeTo: nil |
|
|
|
|
) |
|
|
|
|
UserDefaults.standard.set(data, forKey: "musicFolderBookmark") |
|
|
|
|
} catch { |
|
|
|
|
print("Failed to save bookmark: \(error)") |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private func resolveBookmark() -> URL? { |
|
|
|
|
guard let data = UserDefaults.standard.data(forKey: "musicFolderBookmark") else { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
do { |
|
|
|
|
var isStale = false |
|
|
|
|
let url = try URL( |
|
|
|
|
resolvingBookmarkData: data, |
|
|
|
|
options: .withSecurityScope, |
|
|
|
|
relativeTo: nil, |
|
|
|
|
bookmarkDataIsStale: &isStale |
|
|
|
|
) |
|
|
|
|
guard url.startAccessingSecurityScopedResource() else { return nil } |
|
|
|
|
if isStale { |
|
|
|
|
saveBookmark(for: url) |
|
|
|
|
} |
|
|
|
|
return url |
|
|
|
|
} catch { |
|
|
|
|
return nil |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|