mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Working but NowPlaying slider not working
This commit is contained in:
parent
5829b6d835
commit
80de66b3ad
3 changed files with 43 additions and 69 deletions
|
|
@ -135,7 +135,7 @@ struct MinimalisticMusicPlayerView: View {
|
||||||
|
|
||||||
// Update slider value based on playback
|
// Update slider value based on playback
|
||||||
if musicManager.isPlaying {
|
if musicManager.isPlaying {
|
||||||
let timeSinceLastUpdate = date.timeIntervalSince(musicManager.lastUpdated)
|
let timeSinceLastUpdate = date.timeIntervalSince(musicManager.timestampDate)
|
||||||
let estimatedElapsed = musicManager.elapsedTime + (timeSinceLastUpdate * musicManager.playbackRate)
|
let estimatedElapsed = musicManager.elapsedTime + (timeSinceLastUpdate * musicManager.playbackRate)
|
||||||
return min(estimatedElapsed, musicManager.songDuration)
|
return min(estimatedElapsed, musicManager.songDuration)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -156,8 +156,6 @@ struct MusicControlsView: View {
|
||||||
color: musicManager.avgColor,
|
color: musicManager.avgColor,
|
||||||
dragging: $dragging,
|
dragging: $dragging,
|
||||||
currentDate: timeline.date,
|
currentDate: timeline.date,
|
||||||
lastUpdated: musicManager.lastUpdated,
|
|
||||||
ignoreLastUpdated: musicManager.ignoreLastUpdated,
|
|
||||||
timestampDate: musicManager.timestampDate,
|
timestampDate: musicManager.timestampDate,
|
||||||
elapsedTime: musicManager.elapsedTime,
|
elapsedTime: musicManager.elapsedTime,
|
||||||
playbackRate: musicManager.playbackRate,
|
playbackRate: musicManager.playbackRate,
|
||||||
|
|
@ -285,8 +283,6 @@ struct MusicSliderView: View {
|
||||||
var color: NSColor
|
var color: NSColor
|
||||||
@Binding var dragging: Bool
|
@Binding var dragging: Bool
|
||||||
let currentDate: Date
|
let currentDate: Date
|
||||||
let lastUpdated: Date
|
|
||||||
let ignoreLastUpdated: Bool
|
|
||||||
let timestampDate: Date
|
let timestampDate: Date
|
||||||
let elapsedTime: Double
|
let elapsedTime: Double
|
||||||
let playbackRate: Double
|
let playbackRate: Double
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,6 @@ class MusicManager: ObservableObject {
|
||||||
@Published var albumArt: NSImage = defaultImage
|
@Published var albumArt: NSImage = defaultImage
|
||||||
@Published var isPlaying = false
|
@Published var isPlaying = false
|
||||||
@Published var album: String = "Self Love"
|
@Published var album: String = "Self Love"
|
||||||
@Published var lastUpdated: Date = .distantPast
|
|
||||||
@Published var ignoreLastUpdated = true
|
|
||||||
@Published var isPlayerIdle: Bool = true
|
@Published var isPlayerIdle: Bool = true
|
||||||
@Published var animations: DynamicIslandAnimations = .init()
|
@Published var animations: DynamicIslandAnimations = .init()
|
||||||
@Published var avgColor: NSColor = .white
|
@Published var avgColor: NSColor = .white
|
||||||
|
|
@ -48,9 +46,15 @@ class MusicManager: ObservableObject {
|
||||||
@Published var repeatMode: RepeatMode = .off
|
@Published var repeatMode: RepeatMode = .off
|
||||||
@ObservedObject var coordinator = DynamicIslandViewCoordinator.shared
|
@ObservedObject var coordinator = DynamicIslandViewCoordinator.shared
|
||||||
@Published var usingAppIconForArtwork: Bool = false
|
@Published var usingAppIconForArtwork: Bool = false
|
||||||
|
|
||||||
private var artworkData: Data? = nil
|
private var artworkData: Data? = nil
|
||||||
|
|
||||||
|
// Store last values at the time artwork was changed
|
||||||
|
private var lastArtworkTitle: String = "I'm Handsome"
|
||||||
|
private var lastArtworkArtist: String = "Me"
|
||||||
|
private var lastArtworkAlbum: String = "Self Love"
|
||||||
|
private var lastArtworkBundleIdentifier: String? = nil
|
||||||
|
|
||||||
@Published var isFlipping: Bool = false
|
@Published var isFlipping: Bool = false
|
||||||
private var flipWorkItem: DispatchWorkItem?
|
private var flipWorkItem: DispatchWorkItem?
|
||||||
|
|
||||||
|
|
@ -82,12 +86,16 @@ class MusicManager: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func destroy() {
|
||||||
debounceIdleTask?.cancel()
|
debounceIdleTask?.cancel()
|
||||||
cancellables.removeAll()
|
cancellables.removeAll()
|
||||||
controllerCancellables.removeAll()
|
controllerCancellables.removeAll()
|
||||||
flipWorkItem?.cancel()
|
flipWorkItem?.cancel()
|
||||||
transitionWorkItem?.cancel()
|
transitionWorkItem?.cancel()
|
||||||
|
|
||||||
// Release active controller
|
// Release active controller
|
||||||
activeController = nil
|
activeController = nil
|
||||||
}
|
}
|
||||||
|
|
@ -106,35 +114,22 @@ class MusicManager: ObservableObject {
|
||||||
case .nowPlaying:
|
case .nowPlaying:
|
||||||
// Only create NowPlayingController if not deprecated on this macOS version
|
// Only create NowPlayingController if not deprecated on this macOS version
|
||||||
if !self.isNowPlayingDeprecated {
|
if !self.isNowPlayingDeprecated {
|
||||||
ignoreLastUpdated = false
|
newController = NowPlayingController()
|
||||||
if let controller = NowPlayingController() {
|
|
||||||
// Check if the controller can actually work
|
|
||||||
if controller.isWorking {
|
|
||||||
newController = controller
|
|
||||||
} else {
|
|
||||||
print("⚠️ NowPlayingController created but resources unavailable, falling back to default")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
case .appleMusic:
|
case .appleMusic:
|
||||||
ignoreLastUpdated = true
|
|
||||||
newController = AppleMusicController()
|
newController = AppleMusicController()
|
||||||
case .spotify:
|
case .spotify:
|
||||||
ignoreLastUpdated = true
|
|
||||||
newController = SpotifyController()
|
newController = SpotifyController()
|
||||||
case .youtubeMusic:
|
case .youtubeMusic:
|
||||||
ignoreLastUpdated = true
|
|
||||||
newController = YouTubeMusicController()
|
newController = YouTubeMusicController()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up state observation for the new controller
|
// Set up state observation for the new controller
|
||||||
if let controller = newController {
|
if let controller = newController {
|
||||||
controller.playbackStatePublisher
|
controller.playbackStatePublisher
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] state in
|
.sink { [weak self] state in
|
||||||
guard let self = self,
|
guard let self = self,
|
||||||
self.activeController === controller else { return }
|
self.activeController === controller else { return }
|
||||||
|
|
@ -164,19 +159,8 @@ class MusicManager: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setActiveController(_ controller: any MediaControllerProtocol) {
|
private func setActiveController(_ controller: any MediaControllerProtocol) {
|
||||||
// Transition animation when changing controllers
|
// Set the active controller
|
||||||
transitionWorkItem?.cancel()
|
self.activeController = controller
|
||||||
let workItem = DispatchWorkItem { [weak self] in
|
|
||||||
self?.isTransitioning = true
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
|
||||||
self?.isTransitioning = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
transitionWorkItem = workItem
|
|
||||||
DispatchQueue.main.async(execute: workItem)
|
|
||||||
|
|
||||||
// Set new active controller
|
|
||||||
activeController = controller
|
|
||||||
|
|
||||||
// Get current state from active controller
|
// Get current state from active controller
|
||||||
if let state = Mirror(reflecting: controller).children.first(where: { $0.label == "playbackState" })?.value as? PlaybackState {
|
if let state = Mirror(reflecting: controller).children.first(where: { $0.label == "playbackState" })?.value as? PlaybackState {
|
||||||
|
|
@ -192,7 +176,6 @@ class MusicManager: ObservableObject {
|
||||||
|
|
||||||
// Check for playback state changes (playing/paused)
|
// Check for playback state changes (playing/paused)
|
||||||
if state.isPlaying != self.isPlaying {
|
if state.isPlaying != self.isPlaying {
|
||||||
self.lastUpdated = Date()
|
|
||||||
withAnimation(.smooth) {
|
withAnimation(.smooth) {
|
||||||
self.isPlaying = state.isPlaying
|
self.isPlaying = state.isPlaying
|
||||||
self.updateIdleState(state: state.isPlaying)
|
self.updateIdleState(state: state.isPlaying)
|
||||||
|
|
@ -218,7 +201,7 @@ class MusicManager: ObservableObject {
|
||||||
|
|
||||||
if artworkChanged, let artwork = state.artwork {
|
if artworkChanged, let artwork = state.artwork {
|
||||||
self.updateArtwork(artwork)
|
self.updateArtwork(artwork)
|
||||||
} else if hasContentChange && state.artwork == nil {
|
} else if state.artwork == nil {
|
||||||
// Try to use app icon if no artwork but track changed
|
// Try to use app icon if no artwork but track changed
|
||||||
if let appIconImage = AppIconAsNSImage(for: state.bundleIdentifier) {
|
if let appIconImage = AppIconAsNSImage(for: state.bundleIdentifier) {
|
||||||
self.usingAppIconForArtwork = true
|
self.usingAppIconForArtwork = true
|
||||||
|
|
@ -227,6 +210,14 @@ class MusicManager: ObservableObject {
|
||||||
}
|
}
|
||||||
self.artworkData = state.artwork
|
self.artworkData = state.artwork
|
||||||
|
|
||||||
|
if artworkChanged || state.artwork == nil {
|
||||||
|
// Update last artwork change values
|
||||||
|
self.lastArtworkTitle = state.title
|
||||||
|
self.lastArtworkArtist = state.artist
|
||||||
|
self.lastArtworkAlbum = state.album
|
||||||
|
self.lastArtworkBundleIdentifier = state.bundleIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
// Only update sneak peek if there's actual content and something changed
|
// Only update sneak peek if there's actual content and something changed
|
||||||
if !state.title.isEmpty && !state.artist.isEmpty && state.isPlaying {
|
if !state.title.isEmpty && !state.artist.isEmpty && state.isPlaying {
|
||||||
self.updateSneakPeek()
|
self.updateSneakPeek()
|
||||||
|
|
@ -236,16 +227,18 @@ class MusicManager: ObservableObject {
|
||||||
let timeChanged = state.currentTime != self.elapsedTime
|
let timeChanged = state.currentTime != self.elapsedTime
|
||||||
let durationChanged = state.duration != self.songDuration
|
let durationChanged = state.duration != self.songDuration
|
||||||
let playbackRateChanged = state.playbackRate != self.playbackRate
|
let playbackRateChanged = state.playbackRate != self.playbackRate
|
||||||
|
let shuffleChanged = state.isShuffled != self.isShuffled
|
||||||
|
let repeatModeChanged = state.repeatMode != self.repeatMode
|
||||||
|
|
||||||
if titleChanged {
|
if state.title != self.songTitle {
|
||||||
self.songTitle = state.title
|
self.songTitle = state.title
|
||||||
}
|
}
|
||||||
|
|
||||||
if artistChanged {
|
if state.artist != self.artistName {
|
||||||
self.artistName = state.artist
|
self.artistName = state.artist
|
||||||
}
|
}
|
||||||
|
|
||||||
if albumChanged {
|
if state.album != self.album {
|
||||||
self.album = state.album
|
self.album = state.album
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,30 +254,19 @@ class MusicManager: ObservableObject {
|
||||||
self.playbackRate = state.playbackRate
|
self.playbackRate = state.playbackRate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if shuffleChanged {
|
||||||
|
self.isShuffled = state.isShuffled
|
||||||
|
}
|
||||||
|
|
||||||
if state.bundleIdentifier != self.bundleIdentifier {
|
if state.bundleIdentifier != self.bundleIdentifier {
|
||||||
self.bundleIdentifier = state.bundleIdentifier
|
self.bundleIdentifier = state.bundleIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update shuffle and repeat state from controller
|
if repeatModeChanged {
|
||||||
if state.isShuffled != self.isShuffled {
|
|
||||||
self.isShuffled = state.isShuffled
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.repeatMode != self.repeatMode {
|
|
||||||
self.repeatMode = state.repeatMode
|
self.repeatMode = state.repeatMode
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update timestamp - use current time if state doesn't provide recent timestamp
|
self.timestampDate = state.lastUpdated
|
||||||
let stateTimestamp = state.lastUpdated
|
|
||||||
let now = Date()
|
|
||||||
|
|
||||||
// If the state timestamp is very recent (within 1 second), use it
|
|
||||||
// Otherwise, use current time for better real-time calculation
|
|
||||||
if abs(stateTimestamp.timeIntervalSince(now)) < 1.0 {
|
|
||||||
self.timestampDate = stateTimestamp
|
|
||||||
} else {
|
|
||||||
self.timestampDate = now
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the batch update on the main thread
|
// Execute the batch update on the main thread
|
||||||
|
|
@ -312,9 +294,9 @@ class MusicManager: ObservableObject {
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
if let artworkImage = NSImage(data: artworkData) {
|
if let artworkImage = NSImage(data: artworkData) {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async { [weak self] in
|
||||||
self.usingAppIconForArtwork = false
|
self?.usingAppIconForArtwork = false
|
||||||
self.updateAlbumArt(newAlbumArt: artworkImage)
|
self?.updateAlbumArt(newAlbumArt: artworkImage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -327,14 +309,10 @@ class MusicManager: ObservableObject {
|
||||||
} else {
|
} else {
|
||||||
debounceIdleTask?.cancel()
|
debounceIdleTask?.cancel()
|
||||||
debounceIdleTask = Task { [weak self] in
|
debounceIdleTask = Task { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
try? await Task.sleep(for: .seconds(Defaults[.waitInterval]))
|
try? await Task.sleep(for: .seconds(Defaults[.waitInterval]))
|
||||||
guard let self = self, !Task.isCancelled else { return }
|
withAnimation {
|
||||||
await MainActor.run {
|
self.isPlayerIdle = !self.isPlaying
|
||||||
if self.lastUpdated.timeIntervalSinceNow < -Defaults[.waitInterval] {
|
|
||||||
withAnimation {
|
|
||||||
self.isPlayerIdle = !self.isPlaying
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue