feat: wire PlaylistViewModel into app and content view

feat/music-streaming
Laurent 1 month ago
parent 5b8cdf603c
commit 8f4e330c92
  1. 22
      Music/ContentView.swift
  2. 53
      Music/MusicApp.swift

@ -6,12 +6,18 @@ struct ContentView: View {
var player: PlayerViewModel
var scanner: ScannerService
var audio: AudioService
var playlist: PlaylistViewModel
var body: some View {
VStack(spacing: 0) {
SearchBarView(
trackCount: library.trackCount,
onSearch: { library.search($0) }
trackCount: playlist.selectedPlaylist != nil ? playlist.playlistTracks.count : library.trackCount,
onSearch: { text in
library.search(text)
if playlist.selectedPlaylist != nil {
playlist.search(text)
}
}
)
if scanner.isScanning {
@ -26,15 +32,19 @@ struct ContentView: View {
}
TrackTableView(
tracks: library.tracks,
tracks: playlist.selectedPlaylist != nil ? playlist.playlistTracks : library.tracks,
playingTrackId: player.currentTrack?.id,
onSort: { column in
library.sort(by: column)
if playlist.selectedPlaylist == nil {
library.sort(by: column)
}
},
onDoubleClick: { track in
player.setQueue(library.tracks)
let trackList = playlist.selectedPlaylist != nil ? playlist.playlistTracks : library.tracks
player.setQueue(trackList)
player.play(track)
}
},
onPlayPause: { audio.togglePlayPause() }
)
PlayerControlsView(

@ -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
}
}
}

Loading…
Cancel
Save