From 8f770d08f7ec4dce6290325dd107052168b53fad Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Sun, 3 Aug 2025 22:41:52 +0530 Subject: [PATCH 1/3] Fix: Timeline Playback --- .../components/Notch/NotchHomeView.swift | 26 ++++++++++++++----- DynamicIsland/managers/MusicManager.swift | 12 ++++++++- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/DynamicIsland/components/Notch/NotchHomeView.swift b/DynamicIsland/components/Notch/NotchHomeView.swift index 31a88e4..6f54dd7 100644 --- a/DynamicIsland/components/Notch/NotchHomeView.swift +++ b/DynamicIsland/components/Notch/NotchHomeView.swift @@ -137,7 +137,7 @@ struct MusicControlsView: View { } private var musicSlider: some View { - TimelineView(.animation(minimumInterval: musicManager.playbackRate > 0 ? 0.1 : nil)) { timeline in + TimelineView(.animation(minimumInterval: 0.05)) { timeline in MusicSliderView( sliderValue: $sliderValue, duration: $musicManager.songDuration, @@ -277,11 +277,25 @@ struct MusicSliderView: View { var onValueChange: (Double) -> Void var currentElapsedTime: Double { - // A small buffer is needed to ensure a meaningful difference between the two dates - guard !dragging, timestampDate.timeIntervalSince(lastDragged) > -0.0001, (timestampDate > lastUpdated || ignoreLastUpdated) else { return sliderValue } - let timeDifference = isPlaying ? currentDate.timeIntervalSince(timestampDate) : 0 - let elapsed = elapsedTime + (timeDifference * playbackRate) - return min(elapsed, duration) + // Don't update slider while user is dragging + guard !dragging else { return sliderValue } + + // If not playing, use the current elapsed time from controller + guard isPlaying else { return elapsedTime } + + // For playing media, calculate real-time progress + // Use a more permissive approach for web players + let timeDiff = currentDate.timeIntervalSince(timestampDate) + + // Only use timestamp calculation if we have recent data (within 5 seconds) + // This helps with web players that don't update timestamps frequently + if timeDiff >= 0 && timeDiff < 5.0 && playbackRate > 0 { + let projectedTime = elapsedTime + (timeDiff * playbackRate) + return min(projectedTime, duration) + } else { + // Fallback to controller-provided elapsed time for stale or invalid timestamps + return min(elapsedTime, duration) + } } var body: some View { diff --git a/DynamicIsland/managers/MusicManager.swift b/DynamicIsland/managers/MusicManager.swift index cf141ca..16bb793 100644 --- a/DynamicIsland/managers/MusicManager.swift +++ b/DynamicIsland/managers/MusicManager.swift @@ -274,7 +274,17 @@ class MusicManager: ObservableObject { self.repeatMode = state.repeatMode } - self.timestampDate = state.lastUpdated + // Update timestamp - use current time if state doesn't provide recent timestamp + 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 From 27bb043ef6ca007a4932792758fc17d541c90dbb Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Sun, 3 Aug 2025 23:00:03 +0530 Subject: [PATCH 2/3] Fix: Timeline Reset --- .../components/Notch/NotchHomeView.swift | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/DynamicIsland/components/Notch/NotchHomeView.swift b/DynamicIsland/components/Notch/NotchHomeView.swift index 6f54dd7..906f11b 100644 --- a/DynamicIsland/components/Notch/NotchHomeView.swift +++ b/DynamicIsland/components/Notch/NotchHomeView.swift @@ -97,6 +97,7 @@ struct AlbumArtView: View { struct MusicControlsView: View { @ObservedObject var musicManager = MusicManager.shared + @EnvironmentObject var vm: DynamicIslandViewModel @State private var sliderValue: Double = 0 @State private var dragging: Bool = false @State private var lastDragged: Date = .distantPast @@ -108,6 +109,16 @@ struct MusicControlsView: View { } .buttonStyle(PlainButtonStyle()) .frame(minWidth: Defaults[.showMirror] && Defaults[.showCalendar] ? 140 : 180) + .onAppear { + // Initialize slider value when view appears + sliderValue = musicManager.elapsedTime + } + .onChange(of: vm.notchState) { _, newState in + // Reset slider value when notch opens to prevent stuck state + if newState == .open && !dragging { + sliderValue = musicManager.elapsedTime + } + } } private var songInfoAndSlider: some View { @@ -284,17 +295,31 @@ struct MusicSliderView: View { guard isPlaying else { return elapsedTime } // For playing media, calculate real-time progress - // Use a more permissive approach for web players let timeDiff = currentDate.timeIntervalSince(timestampDate) - // Only use timestamp calculation if we have recent data (within 5 seconds) - // This helps with web players that don't update timestamps frequently - if timeDiff >= 0 && timeDiff < 5.0 && playbackRate > 0 { + // Use real-time calculation for positive time differences with reasonable bounds + // This provides smooth progression for all media sources + if timeDiff >= 0 && timeDiff < 30.0 && playbackRate > 0 { let projectedTime = elapsedTime + (timeDiff * playbackRate) - return min(projectedTime, duration) + let clampedTime = min(projectedTime, duration) + + // Ensure we don't go backwards unless there's a significant jump + if clampedTime >= sliderValue || abs(clampedTime - sliderValue) > 2.0 { + return clampedTime + } else { + return sliderValue + } } else { - // Fallback to controller-provided elapsed time for stale or invalid timestamps - return min(elapsedTime, duration) + // For very stale timestamps or negative differences, use controller time + // but only if it makes sense relative to current slider position + let controllerTime = min(elapsedTime, duration) + + // Prevent backwards jumps unless there's a significant change (track change, seek) + if controllerTime >= sliderValue || abs(controllerTime - sliderValue) > 5.0 { + return controllerTime + } else { + return sliderValue + } } } @@ -324,6 +349,19 @@ struct MusicSliderView: View { .onChange(of: currentDate) { sliderValue = currentElapsedTime } + .onChange(of: elapsedTime) { + // Update slider when media changes (e.g., track changes, seeking) + // But only if we're not dragging and the difference is significant + if !dragging && abs(elapsedTime - sliderValue) > 1.0 { + sliderValue = elapsedTime + } + } + .onChange(of: duration) { + // Handle track changes - reset slider if duration changes significantly + if !dragging && sliderValue > duration { + sliderValue = min(elapsedTime, duration) + } + } } func timeString(from seconds: Double) -> String { From c0b11f86a5616f8e93736823a0e5efd061d7fcec Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Sun, 3 Aug 2025 23:20:31 +0530 Subject: [PATCH 3/3] Fix: Jump Progress --- .../components/Notch/NotchHomeView.swift | 51 ++----------------- 1 file changed, 5 insertions(+), 46 deletions(-) diff --git a/DynamicIsland/components/Notch/NotchHomeView.swift b/DynamicIsland/components/Notch/NotchHomeView.swift index 906f11b..c494630 100644 --- a/DynamicIsland/components/Notch/NotchHomeView.swift +++ b/DynamicIsland/components/Notch/NotchHomeView.swift @@ -288,39 +288,11 @@ struct MusicSliderView: View { var onValueChange: (Double) -> Void var currentElapsedTime: Double { - // Don't update slider while user is dragging - guard !dragging else { return sliderValue } - - // If not playing, use the current elapsed time from controller - guard isPlaying else { return elapsedTime } - - // For playing media, calculate real-time progress - let timeDiff = currentDate.timeIntervalSince(timestampDate) - - // Use real-time calculation for positive time differences with reasonable bounds - // This provides smooth progression for all media sources - if timeDiff >= 0 && timeDiff < 30.0 && playbackRate > 0 { - let projectedTime = elapsedTime + (timeDiff * playbackRate) - let clampedTime = min(projectedTime, duration) - - // Ensure we don't go backwards unless there's a significant jump - if clampedTime >= sliderValue || abs(clampedTime - sliderValue) > 2.0 { - return clampedTime - } else { - return sliderValue - } - } else { - // For very stale timestamps or negative differences, use controller time - // but only if it makes sense relative to current slider position - let controllerTime = min(elapsedTime, duration) - - // Prevent backwards jumps unless there's a significant change (track change, seek) - if controllerTime >= sliderValue || abs(controllerTime - sliderValue) > 5.0 { - return controllerTime - } else { - return sliderValue - } - } + // A small buffer is needed to ensure a meaningful difference between the two dates + guard !dragging, timestampDate.timeIntervalSince(lastDragged) > -1 else { return sliderValue } + let timeDifference = isPlaying ? currentDate.timeIntervalSince(timestampDate) : 0 + let elapsed = elapsedTime + (timeDifference * playbackRate) + return min(elapsed, duration) } var body: some View { @@ -349,19 +321,6 @@ struct MusicSliderView: View { .onChange(of: currentDate) { sliderValue = currentElapsedTime } - .onChange(of: elapsedTime) { - // Update slider when media changes (e.g., track changes, seeking) - // But only if we're not dragging and the difference is significant - if !dragging && abs(elapsedTime - sliderValue) > 1.0 { - sliderValue = elapsedTime - } - } - .onChange(of: duration) { - // Handle track changes - reset slider if duration changes significantly - if !dragging && sliderValue > duration { - sliderValue = min(elapsedTime, duration) - } - } } func timeString(from seconds: Double) -> String {