Open thekryz opened 4 hours ago
How about something along these lines @ iTunesRadioStation.swift:
class LeadingEdgeDebouncer {
private let delay: TimeInterval
private var workItem: DispatchWorkItem?
private var lastExecutionTime: Date?
init(delay: TimeInterval) {
self.delay = delay
}
func debounce(_ block: @escaping () -> Void) {
// Cancel any pending execution
workItem?.cancel()
let now = Date()
if lastExecutionTime == nil || now.timeIntervalSince(lastExecutionTime!) > delay {
// Execute immediately if it's the first call or if enough time has passed since the last execution
lastExecutionTime = now
block()
} else {
// Debounce subsequent calls
let newWorkItem = DispatchWorkItem {
self.lastExecutionTime = Date()
block()
}
workItem = newWorkItem
// Schedule the work item to be executed after the specified delay
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: newWorkItem)
}
}
}
extension iTunesRadioStation {
// Create a debouncer with a 0.3 second delay
private let ratingDebouncer = LeadingEdgeDebouncer(delay: 0.3)
func setRating(_ rating: Int) {
// Invalidate any existing timer
debounceSetRatingTimer?.invalidate()
// Ensure we have a current track to rate
guard latestPlayInfo != nil || !(iTunes?.currentTrack?.name ?? "").isEmpty else {
os_log("%{public}s[%{public}ld], %{public}s: try to set rating but no current track info", ((#file as NSString).lastPathComponent), #line, #function)
return
}
let name = latestPlayInfo?.name ?? iTunes?.currentTrack?.name ?? "nil"
os_log("%{public}s[%{public}ld], %{public}s: debouncing set rating for %{public}s %{public}ld…", ((#file as NSString).lastPathComponent), #line, #function, name, rating)
// Use the debouncer to handle the rating change
ratingDebouncer.debounce { [weak self] in
guard let self = self else { return }
let track = self.iTunes?.currentTrack
// Set the rating on the track
track?.setRating?(rating)
os_log("%{public}s[%{public}ld], %{public}s: … set %{public}s rating %{public}ld", ((#file as NSString).lastPathComponent), #line, #function, track?.name ?? "nil", rating)
}
}
}
this should implement a Leading Edge Debouncer which will:
This way the app would instantly transfer the initial rating while protecting against potential issues from rapid successive changes.
Hi there! Thank you for your app, I use it a lot! I have a huge unrated library of music, so I rate a lot. Often, I want to rate songs quickly and jump to the next one. But there is a delay - and checking your code, it's there deliberately to "debounce" (?) for 2s in the setRating function. Is this really (still) necessary? I would appreciate an immediate (or only slightly) delayed rating transferral. Would removing the debouncing be possible or what issues does it lead to?