diff --git a/DynamicIsland/MediaControllers/NowPlayingController.swift b/DynamicIsland/MediaControllers/NowPlayingController.swift index 4975bdf..1b863b7 100644 --- a/DynamicIsland/MediaControllers/NowPlayingController.swift +++ b/DynamicIsland/MediaControllers/NowPlayingController.swift @@ -166,6 +166,19 @@ final class NowPlayingController: ObservableObject, MediaControllerProtocol { let pipeHandler = JSONLinesPipeHandler() process.standardOutput = await pipeHandler.getPipe() + + // Capture stderr so framework/script errors are logged + let stderrPipe = Pipe() + process.standardError = stderrPipe + stderrPipe.fileHandleForReading.readabilityHandler = { handle in + let data = handle.availableData + guard !data.isEmpty, + let message = String(data: data, encoding: .utf8)? + .trimmingCharacters(in: .whitespacesAndNewlines), + !message.isEmpty + else { return } + print("NowPlayingController [stderr]: \(message)") + } self.process = process self.pipeHandler = pipeHandler diff --git a/DynamicIsland/helpers/MediaChecker.swift b/DynamicIsland/helpers/MediaChecker.swift index 8c23eeb..af40a1d 100644 --- a/DynamicIsland/helpers/MediaChecker.swift +++ b/DynamicIsland/helpers/MediaChecker.swift @@ -42,13 +42,23 @@ final class MediaChecker: Sendable { let nowPlayingTestClientPath = Bundle.main.bundleURL - .appendingPathComponent("Contents/Helpers/MediaRemoteAdapterTestClient") + .appendingPathComponent("Contents/Helpers/NowPlayingTestClient") .path + guard FileManager.default.isExecutableFile(atPath: nowPlayingTestClientPath) else { + throw MediaCheckerError.missingResources + } + let process = Process() process.executableURL = URL(fileURLWithPath: "/usr/bin/perl") process.arguments = [scriptURL.path, frameworkPath, nowPlayingTestClientPath, "test"] + // Capture stderr to distinguish script errors from genuine deprecation. + // The perl script's fail() and the framework's test function both exit + // with status 1, but only script-level failures write to stderr. + let stderrPipe = Pipe() + process.standardError = stderrPipe + do { try process.run() } catch { @@ -81,6 +91,18 @@ final class MediaChecker: Sendable { throw MediaCheckerError.timeout } + let stderrData = stderrPipe.fileHandleForReading.readDataToEndOfFile() + let stderrOutput = String(data: stderrData, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" + + // If the process exited with a non-zero status AND stderr contains + // output, a script-level error occurred (e.g. framework failed to load, + // symbol not found). Treat this as an execution failure rather than + // reporting deprecated, since perl's fail() also uses exit(1). + if process.terminationStatus != 0, !stderrOutput.isEmpty { + print("MediaChecker: script error (exit \(process.terminationStatus)): \(stderrOutput)") + throw MediaCheckerError.processExecutionFailed + } + let isDeprecated = process.terminationStatus == 1 return isDeprecated }