1import AppKit23@MainActor4protocol WallpaperManaging {5 func download(from url: URL) async throws -> URL6 func setDesktopImage(from fileURL: URL, screen: NSScreen?) throws7 func saveToFolder(from sourceURL: URL, to folderURL: URL) throws8}910@MainActor11protocol DesktopImageSetting {12 func setDesktopImageURL(13 _ url: URL, for screen: NSScreen, options: [NSWorkspace.DesktopImageOptionKey: Any]14 ) throws15}1617extension NSWorkspace: DesktopImageSetting {}1819class WallpaperManager: WallpaperManaging {20 static let shared = WallpaperManager()2122 private let session: URLSession23 private let desktopSetter: @MainActor (URL, NSScreen) throws -> Void2425 init(26 session: URLSession = .shared,27 desktopSetter: (@MainActor (URL, NSScreen) throws -> Void)? = nil,28 workspace: DesktopImageSetting = NSWorkspace.shared29 ) {30 self.session = session31 self.desktopSetter =32 desktopSetter ?? { url, screen in33 try workspace.setDesktopImageURL(34 url, for: screen, options: [:]35 )36 }37 }3839 40 41 42 43 44 45 46 func download(from url: URL) async throws -> URL {47 let (data, response) = try await session.data(from: url)48 guard let response = response as? HTTPURLResponse,49 (200...299).contains(response.statusCode)50 else {51 throw WallhavenError.apiError("Wallpaper download failed")52 }5354 let fileURL = Self.makeTempURL(for: url)55 try data.write(to: fileURL)56 return fileURL57 }5859 60 61 62 63 64 static func makeTempURL(for url: URL) -> URL {65 let fileExtension = url.pathExtension.isEmpty ? "img" : url.pathExtension66 let stem = UUID().uuidString67 let filename = "haven-\(stem).\(fileExtension)"68 return FileManager.default.temporaryDirectory.appendingPathComponent(filename)69 }7071 72 @MainActor73 func setDesktopImage(from fileURL: URL, screen: NSScreen? = nil) throws {74 if let screen = screen {75 try desktopSetter(fileURL, screen)76 } else {77 78 79 80 let targetScreen = NSScreen.main ?? NSScreen.screens.first!81 try desktopSetter(fileURL, targetScreen)82 }83 }8485 86 87 88 89 90 91 92 93 94 func saveToFolder(from sourceURL: URL, to folderURL: URL) throws {95 96 try FileManager.default.createDirectory(at: folderURL, withIntermediateDirectories: true)9798 let destinationURL = folderURL.appendingPathComponent(sourceURL.lastPathComponent)99100 if FileManager.default.fileExists(atPath: destinationURL.path) {101 let sourceData = try Data(contentsOf: sourceURL)102 let existingData = try Data(contentsOf: destinationURL)103 if sourceData == existingData {104 return105 }106 try FileManager.default.removeItem(at: destinationURL)107 }108109 try FileManager.default.copyItem(at: sourceURL, to: destinationURL)110 }111112 113 @MainActor114 func setWallpaper(from url: URL, screen: NSScreen? = nil) async throws {115 let localURL = try await download(from: url)116 try setDesktopImage(from: localURL, screen: screen)117 }118}