mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Atoll: deliver Spotify Canvas as lockscreen wallpaper without album-art flash
Two fixes for the right-click-artwork-to-wallpaper flow on Spotify tracks that have a Canvas video. 1. Force AllSpacesAndDisplays.Type and SystemDefault.Type to "individual" while the artwork is active (was: keep existing value via nil-coalescing). On macOS Tahoe the user-level wallpaper rotation honors the parent Type field, so leaving it as "folder" caused WallpaperAgent to advance to the next image in a rotating wallpaper pack instead of honoring the patched single-image / aerial choice. The original Type is preserved in the plist backup at temp/atoll_wallpaper_backup.plist and is restored byte-for-byte on dismiss, so rotation resumes normally after the artwork is dismissed. The forced override is also applied to nested Displays / Spaces blocks but only when Type was already present, to avoid mutating block shapes that intentionally omit it. 2. When the Canvas is expected at right-click time (effectiveVideoURL != nil), defer applyArtworkToPlist and restartWallpaperAgent entirely. Previously the flow applied raw album art as the static wallpaper, restarted the agent, then prepared and applied the aerial in a second restart, causing a visible flash of the raw album cover. The deferred path stores the blurred fallback URL in pendingFallbackStaticURL and only touches the plist once: with the aerial on success, or with the blurred album cover via applyDeferredStaticFallback if the aerial asset prep or aerial-plist patch fails. The in-app videoWindow continues to mask the desktop wallpaper during prep on the unlocked screen; the lock screen simply transitions from the existing wallpaper straight to the Canvas video. The earlier ?? nil-coalescing in patchWallpaperEntries also covered per-Display, per-Space.Default, and per-Space.Displays Type fields, so those are forced too where present. Signed-off-by: Jis G Jacob <studiokeys@blissroms.org>
This commit is contained in:
parent
d1d9d75ba8
commit
a8d7dff322
1 changed files with 69 additions and 16 deletions
|
|
@ -324,6 +324,8 @@ final class FullScreenArtworkWindowManager: ObservableObject {
|
||||||
private var isLiveWallpaperAllowed = false
|
private var isLiveWallpaperAllowed = false
|
||||||
private var activeLiveWallpaperFingerprint: String?
|
private var activeLiveWallpaperFingerprint: String?
|
||||||
private var activeWallpaperKey: String?
|
private var activeWallpaperKey: String?
|
||||||
|
private var pendingFallbackStaticURL: URL?
|
||||||
|
private var pendingFallbackWallpaperKey: String?
|
||||||
private var fallbackRightClickMonitor: Any?
|
private var fallbackRightClickMonitor: Any?
|
||||||
private var artworkLayoutOverCanvasPreferenceCancellable: AnyCancellable?
|
private var artworkLayoutOverCanvasPreferenceCancellable: AnyCancellable?
|
||||||
private var lyricsTextCancellable: AnyCancellable?
|
private var lyricsTextCancellable: AnyCancellable?
|
||||||
|
|
@ -358,6 +360,8 @@ final class FullScreenArtworkWindowManager: ObservableObject {
|
||||||
isLiveWallpaperAllowed = false
|
isLiveWallpaperAllowed = false
|
||||||
activeLiveWallpaperFingerprint = nil
|
activeLiveWallpaperFingerprint = nil
|
||||||
activeWallpaperKey = nil
|
activeWallpaperKey = nil
|
||||||
|
pendingFallbackStaticURL = nil
|
||||||
|
pendingFallbackWallpaperKey = nil
|
||||||
let shouldRestoreStandardPanelLayout = isShowingSpotifyCanvasFallback
|
let shouldRestoreStandardPanelLayout = isShowingSpotifyCanvasFallback
|
||||||
isShowingSpotifyCanvasFallback = false
|
isShowingSpotifyCanvasFallback = false
|
||||||
removeFallbackRightClickMonitor()
|
removeFallbackRightClickMonitor()
|
||||||
|
|
@ -439,12 +443,14 @@ final class FullScreenArtworkWindowManager: ObservableObject {
|
||||||
let previousFallbackState = isShowingSpotifyCanvasFallback
|
let previousFallbackState = isShowingSpotifyCanvasFallback
|
||||||
|
|
||||||
guard let rawArtworkFileURL = persistedArtworkFileURL(for: artwork, fingerprint: artworkFingerprint) else { return }
|
guard let rawArtworkFileURL = persistedArtworkFileURL(for: artwork, fingerprint: artworkFingerprint) else { return }
|
||||||
let wallpaperURL = shouldUseStaticFallbackWallpaper
|
let expectCanvas = effectiveVideoURL != nil
|
||||||
? blurredWallpaperFileURL(for: artwork, fingerprint: artworkFingerprint, screen: screen) ?? rawArtworkFileURL
|
let blurredFallbackURL = blurredWallpaperFileURL(for: artwork, fingerprint: artworkFingerprint, screen: screen) ?? rawArtworkFileURL
|
||||||
|
let wallpaperURL = (shouldUseStaticFallbackWallpaper || expectCanvas)
|
||||||
|
? blurredFallbackURL
|
||||||
: rawArtworkFileURL
|
: rawArtworkFileURL
|
||||||
let wallpaperKey = wallpaperIdentityKey(
|
let wallpaperKey = wallpaperIdentityKey(
|
||||||
fingerprint: artworkFingerprint,
|
fingerprint: artworkFingerprint,
|
||||||
usesFallback: shouldUseStaticFallbackWallpaper,
|
usesFallback: shouldUseStaticFallbackWallpaper || expectCanvas,
|
||||||
screen: screen
|
screen: screen
|
||||||
)
|
)
|
||||||
let desiredLiveWallpaperFingerprint = effectiveVideoURL?.absoluteString
|
let desiredLiveWallpaperFingerprint = effectiveVideoURL?.absoluteString
|
||||||
|
|
@ -459,6 +465,12 @@ final class FullScreenArtworkWindowManager: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
if requiresWallpaperRefresh {
|
if requiresWallpaperRefresh {
|
||||||
|
if expectCanvas {
|
||||||
|
pendingFallbackStaticURL = blurredFallbackURL
|
||||||
|
pendingFallbackWallpaperKey = wallpaperKey
|
||||||
|
activeWallpaperKey = nil
|
||||||
|
activeLiveWallpaperFingerprint = nil
|
||||||
|
} else {
|
||||||
showWallpaperTransition(on: screen, imageURL: wallpaperURL)
|
showWallpaperTransition(on: screen, imageURL: wallpaperURL)
|
||||||
|
|
||||||
guard applyArtworkToPlist(imageURL: wallpaperURL) else {
|
guard applyArtworkToPlist(imageURL: wallpaperURL) else {
|
||||||
|
|
@ -470,6 +482,9 @@ final class FullScreenArtworkWindowManager: ObservableObject {
|
||||||
restartWallpaperAgent()
|
restartWallpaperAgent()
|
||||||
activeWallpaperKey = wallpaperKey
|
activeWallpaperKey = wallpaperKey
|
||||||
activeLiveWallpaperFingerprint = nil
|
activeLiveWallpaperFingerprint = nil
|
||||||
|
pendingFallbackStaticURL = nil
|
||||||
|
pendingFallbackWallpaperKey = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isShowing = true
|
isShowing = true
|
||||||
|
|
@ -773,6 +788,27 @@ final class FullScreenArtworkWindowManager: ObservableObject {
|
||||||
return finalBitmap.representation(using: .png, properties: [:])
|
return finalBitmap.representation(using: .png, properties: [:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func applyDeferredStaticFallback() {
|
||||||
|
guard let url = pendingFallbackStaticURL else { return }
|
||||||
|
let key = pendingFallbackWallpaperKey
|
||||||
|
pendingFallbackStaticURL = nil
|
||||||
|
pendingFallbackWallpaperKey = nil
|
||||||
|
|
||||||
|
if let screen = NSScreen.main {
|
||||||
|
showWallpaperTransition(on: screen, imageURL: url)
|
||||||
|
}
|
||||||
|
guard applyArtworkToPlist(imageURL: url) else {
|
||||||
|
print("[FullScreenArtworkWindowManager] Failed to apply deferred static fallback")
|
||||||
|
hideWallpaperTransition()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
restartWallpaperAgent()
|
||||||
|
activeWallpaperKey = key
|
||||||
|
activeLiveWallpaperFingerprint = nil
|
||||||
|
hideVideoWindow()
|
||||||
|
scheduleWallpaperTransitionHide(after: .milliseconds(900))
|
||||||
|
}
|
||||||
|
|
||||||
private func scheduleLiveWallpaperPreparation(for videoURL: URL?, artwork: NSImage, identifier: String) {
|
private func scheduleLiveWallpaperPreparation(for videoURL: URL?, artwork: NSImage, identifier: String) {
|
||||||
liveWallpaperTask?.cancel()
|
liveWallpaperTask?.cancel()
|
||||||
liveWallpaperTask = nil
|
liveWallpaperTask = nil
|
||||||
|
|
@ -837,10 +873,18 @@ final class FullScreenArtworkWindowManager: ObservableObject {
|
||||||
guard currentIdentifier == identifier else { return }
|
guard currentIdentifier == identifier else { return }
|
||||||
guard currentVideoURL?.absoluteString == nextFingerprint else { return }
|
guard currentVideoURL?.absoluteString == nextFingerprint else { return }
|
||||||
guard !self.shouldUseSpotifyStaticFallbackWallpaper(videoURL: currentVideoURL) else { return }
|
guard !self.shouldUseSpotifyStaticFallbackWallpaper(videoURL: currentVideoURL) else { return }
|
||||||
guard prepared else { return }
|
guard prepared else {
|
||||||
guard self.applyAerialToPlist(assetID: assetID) else { return }
|
self.applyDeferredStaticFallback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard self.applyAerialToPlist(assetID: assetID) else {
|
||||||
|
self.applyDeferredStaticFallback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
self.activeLiveWallpaperFingerprint = nextFingerprint
|
self.activeLiveWallpaperFingerprint = nextFingerprint
|
||||||
|
self.pendingFallbackStaticURL = nil
|
||||||
|
self.pendingFallbackWallpaperKey = nil
|
||||||
self.restartWallpaperAgent()
|
self.restartWallpaperAgent()
|
||||||
self.scheduleHideVideoWindow(after: .milliseconds(950), expectedURL: videoURL)
|
self.scheduleHideVideoWindow(after: .milliseconds(950), expectedURL: videoURL)
|
||||||
print("[FullScreenArtworkWindowManager] Live wallpaper applied")
|
print("[FullScreenArtworkWindowManager] Live wallpaper applied")
|
||||||
|
|
@ -1549,13 +1593,13 @@ final class FullScreenArtworkWindowManager: ObservableObject {
|
||||||
var allSpaces = (plist["AllSpacesAndDisplays"] as? [String: Any]) ?? [:]
|
var allSpaces = (plist["AllSpacesAndDisplays"] as? [String: Any]) ?? [:]
|
||||||
allSpaces["Desktop"] = desktopBlock
|
allSpaces["Desktop"] = desktopBlock
|
||||||
allSpaces["Idle"] = idleBlock
|
allSpaces["Idle"] = idleBlock
|
||||||
allSpaces["Type"] = allSpaces["Type"] ?? "individual"
|
allSpaces["Type"] = "individual"
|
||||||
plist["AllSpacesAndDisplays"] = allSpaces
|
plist["AllSpacesAndDisplays"] = allSpaces
|
||||||
|
|
||||||
var systemDefault = (plist["SystemDefault"] as? [String: Any]) ?? [:]
|
var systemDefault = (plist["SystemDefault"] as? [String: Any]) ?? [:]
|
||||||
systemDefault["Desktop"] = desktopBlock
|
systemDefault["Desktop"] = desktopBlock
|
||||||
systemDefault["Idle"] = idleBlock
|
systemDefault["Idle"] = idleBlock
|
||||||
systemDefault["Type"] = systemDefault["Type"] ?? "individual"
|
systemDefault["Type"] = "individual"
|
||||||
plist["SystemDefault"] = systemDefault
|
plist["SystemDefault"] = systemDefault
|
||||||
|
|
||||||
if var displays = plist["Displays"] as? [String: Any] {
|
if var displays = plist["Displays"] as? [String: Any] {
|
||||||
|
|
@ -1563,6 +1607,9 @@ final class FullScreenArtworkWindowManager: ObservableObject {
|
||||||
if var display = displays[key] as? [String: Any] {
|
if var display = displays[key] as? [String: Any] {
|
||||||
display["Desktop"] = desktopBlock
|
display["Desktop"] = desktopBlock
|
||||||
display["Idle"] = idleBlock
|
display["Idle"] = idleBlock
|
||||||
|
if display["Type"] != nil {
|
||||||
|
display["Type"] = "individual"
|
||||||
|
}
|
||||||
displays[key] = display
|
displays[key] = display
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1575,6 +1622,9 @@ final class FullScreenArtworkWindowManager: ObservableObject {
|
||||||
if var defaultEntry = space["Default"] as? [String: Any] {
|
if var defaultEntry = space["Default"] as? [String: Any] {
|
||||||
defaultEntry["Desktop"] = desktopBlock
|
defaultEntry["Desktop"] = desktopBlock
|
||||||
defaultEntry["Idle"] = idleBlock
|
defaultEntry["Idle"] = idleBlock
|
||||||
|
if defaultEntry["Type"] != nil {
|
||||||
|
defaultEntry["Type"] = "individual"
|
||||||
|
}
|
||||||
space["Default"] = defaultEntry
|
space["Default"] = defaultEntry
|
||||||
}
|
}
|
||||||
if var spaceDisplays = space["Displays"] as? [String: Any] {
|
if var spaceDisplays = space["Displays"] as? [String: Any] {
|
||||||
|
|
@ -1582,6 +1632,9 @@ final class FullScreenArtworkWindowManager: ObservableObject {
|
||||||
if var display = spaceDisplays[displayKey] as? [String: Any] {
|
if var display = spaceDisplays[displayKey] as? [String: Any] {
|
||||||
display["Desktop"] = desktopBlock
|
display["Desktop"] = desktopBlock
|
||||||
display["Idle"] = idleBlock
|
display["Idle"] = idleBlock
|
||||||
|
if display["Type"] != nil {
|
||||||
|
display["Type"] = "individual"
|
||||||
|
}
|
||||||
spaceDisplays[displayKey] = display
|
spaceDisplays[displayKey] = display
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue