fix: block app termination while recording is finalizing
- RecorderState.isFinalizing flag set during stopRecording → finishWriting - applicationShouldTerminate returns .terminateLater if finalizing - Shows alert '正在保存录制文件...' to inform user - After save dialog completes, calls NSApp.reply(toApplicationShouldTerminate: true) - All failure paths also check and resolve pendingTerminate - Fixes: 9-min recording lost because user closed app before async finishWriting completed
This commit is contained in:
parent
eb3e98d567
commit
d4a39dd7c3
@ -28,6 +28,22 @@ class AppDel: NSObject, NSApplicationDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
|
||||
if RecorderState.isFinalizing {
|
||||
NSLog("[MiniPlayer] ⏳ Termination blocked: recording is finalizing")
|
||||
RecorderState.pendingTerminate = true
|
||||
// 显示提示
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "正在保存录制文件..."
|
||||
alert.informativeText = "请等待录制保存完成后再关闭应用。"
|
||||
alert.alertStyle = .informational
|
||||
alert.addButton(withTitle: "确定")
|
||||
alert.runModal()
|
||||
return .terminateLater
|
||||
}
|
||||
return .terminateNow
|
||||
}
|
||||
|
||||
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -162,6 +162,12 @@ private func tapProcessCallback(
|
||||
|
||||
// MARK: - PlayerRecorder
|
||||
|
||||
/// 全局状态:录制是否正在 finalize(阻止应用退出)
|
||||
enum RecorderState {
|
||||
static nonisolated(unsafe) var isFinalizing: Bool = false
|
||||
static nonisolated(unsafe) var pendingTerminate: Bool = false
|
||||
}
|
||||
|
||||
/// 视频源录制器:AVPlayerItemVideoOutput 抓帧 + MTAudioProcessingTap 捕获音频
|
||||
/// 边录边写文件,录制结束后仅改名和移动
|
||||
@MainActor
|
||||
@ -287,6 +293,7 @@ final class PlayerRecorder: NSObject {
|
||||
guard isRecording else { return }
|
||||
isRecording = false
|
||||
isRunning = false
|
||||
RecorderState.isFinalizing = true // 阻止应用退出,直到 finishWriting 完成
|
||||
captureTimer?.invalidate()
|
||||
captureTimer = nil
|
||||
stopDurationTimer()
|
||||
@ -314,8 +321,14 @@ final class PlayerRecorder: NSObject {
|
||||
writer?.finishWriting { [weak self] in
|
||||
NSLog("[Recorder] finishWriting callback invoked")
|
||||
DispatchQueue.main.async {
|
||||
RecorderState.isFinalizing = false // 允许应用退出了
|
||||
|
||||
guard let self = self, let w = self.writer else {
|
||||
NSLog("[Recorder] ⚠️ self or writer is nil in finishWriting callback")
|
||||
if RecorderState.pendingTerminate {
|
||||
RecorderState.pendingTerminate = false
|
||||
NSApp.reply(toApplicationShouldTerminate: true)
|
||||
}
|
||||
return
|
||||
}
|
||||
let vFrames = self.captureFrameCount
|
||||
@ -333,6 +346,7 @@ final class PlayerRecorder: NSObject {
|
||||
} else {
|
||||
NSLog("[Recorder] ⚠️ Temp file missing: %@", tempURL.path)
|
||||
self.onError?("Recording file missing")
|
||||
self.checkPendingTerminate()
|
||||
}
|
||||
} else {
|
||||
NSLog("[Recorder] ✗ finishWriting failed: status=%d, error=%@",
|
||||
@ -341,6 +355,7 @@ final class PlayerRecorder: NSObject {
|
||||
if let url = self.tempURL {
|
||||
try? FileManager.default.removeItem(at: url)
|
||||
}
|
||||
self.checkPendingTerminate()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -632,7 +647,13 @@ final class PlayerRecorder: NSObject {
|
||||
|
||||
panel.begin { [weak self] response in
|
||||
Task { @MainActor in
|
||||
guard let self = self else { return }
|
||||
guard let self = self else {
|
||||
if RecorderState.pendingTerminate {
|
||||
RecorderState.pendingTerminate = false
|
||||
NSApp.reply(toApplicationShouldTerminate: true)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if response == .OK, let finalURL = panel.url {
|
||||
do {
|
||||
@ -653,9 +674,18 @@ final class PlayerRecorder: NSObject {
|
||||
NSLog("[Recorder] User cancelled save, deleting temp file")
|
||||
try? FileManager.default.removeItem(at: tempURL)
|
||||
}
|
||||
self.checkPendingTerminate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func checkPendingTerminate() {
|
||||
if RecorderState.pendingTerminate {
|
||||
RecorderState.pendingTerminate = false
|
||||
NSLog("[MiniPlayer] Pending terminate triggered, replying to terminate")
|
||||
NSApp.reply(toApplicationShouldTerminate: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user