Surface whether visual capture ran on the last session
Visual capture falls back to audio-only silently, so the user couldn't tell if it attached on a real call. SessionInfo now carries visualSegmentCount (nil = audio-only; a count = visual ran, with that many vision-detected speaker segments), shown in the menu as '… · N visual segments' or '… · audio-only'. Makes the pending live-call validation unambiguous.
This commit is contained in:
@@ -8,6 +8,10 @@ struct SessionInfo: Equatable {
|
||||
let mixedURL: URL
|
||||
let duration: Double
|
||||
let selfSpanCount: Int
|
||||
/// Count of vision-detected speaker segments if visual capture attached, or nil
|
||||
/// if the session was audio-only (no adapter / no window / capture failed). Lets
|
||||
/// the user see at a glance whether the visual pipeline ran on a real call.
|
||||
let visualSegmentCount: Int?
|
||||
}
|
||||
|
||||
/// Owns a single recording session: creates the session folder, drives
|
||||
@@ -268,18 +272,21 @@ final class SessionController: ObservableObject {
|
||||
|
||||
/// Stop visual capture (if any), write `visual_timeline.json`, and return the
|
||||
/// timeline for the backend: visual segments + merged self-spans when visual
|
||||
/// ran, otherwise the mic-VAD self spans alone.
|
||||
private func stopVisualAndTimeline(_ result: RecordingResult, folder: URL?) async -> [VisualTimeline.Segment] {
|
||||
/// ran, otherwise the mic-VAD self spans alone. `visualRan` reports whether the
|
||||
/// visual pipeline actually attached (for the after-session indicator).
|
||||
private func stopVisualAndTimeline(_ result: RecordingResult, folder: URL?)
|
||||
async -> (timeline: [VisualTimeline.Segment], visualRan: Bool) {
|
||||
let selfName = settings.selfName
|
||||
if let vc = visualCapture, let folder {
|
||||
visualCapture = nil
|
||||
return await vc.finish(
|
||||
let timeline = await vc.finish(
|
||||
selfSpans: result.selfSpans, selfName: selfName,
|
||||
sessionId: folder.lastPathComponent, t0Unix: result.t0Unix,
|
||||
durationSec: result.duration, folder: folder)
|
||||
return (timeline, true)
|
||||
}
|
||||
if let vc = visualCapture { await vc.cancel(); visualCapture = nil }
|
||||
return TranscriptPipeline.timeline(fromSelfSpans: result.selfSpans, selfName: selfName)
|
||||
return (TranscriptPipeline.timeline(fromSelfSpans: result.selfSpans, selfName: selfName), false)
|
||||
}
|
||||
|
||||
private func stop() {
|
||||
@@ -290,12 +297,12 @@ final class SessionController: ObservableObject {
|
||||
lifecycleGeneration += 1
|
||||
lifecycleTask = Task {
|
||||
let result = await recorder.stop()
|
||||
let timeline = await self.stopVisualAndTimeline(result, folder: folder)
|
||||
self.finish(result, timeline: timeline)
|
||||
let visual = await self.stopVisualAndTimeline(result, folder: folder)
|
||||
self.finish(result, timeline: visual.timeline, visualRan: visual.visualRan)
|
||||
}
|
||||
}
|
||||
|
||||
private func finish(_ result: RecordingResult, timeline: [VisualTimeline.Segment]) {
|
||||
private func finish(_ result: RecordingResult, timeline: [VisualTimeline.Segment], visualRan: Bool) {
|
||||
recorder = nil
|
||||
micLevel = 0
|
||||
systemLevel = 0
|
||||
@@ -303,9 +310,11 @@ final class SessionController: ObservableObject {
|
||||
transcriptStatus = .idle
|
||||
if let folder = currentFolder {
|
||||
writeSelfSpans(result, to: folder)
|
||||
let visualCount = visualRan ? timeline.filter { $0.source == "vision" }.count : nil
|
||||
lastSession = SessionInfo(
|
||||
folder: folder, mixedURL: result.mixedURL,
|
||||
duration: result.duration, selfSpanCount: result.selfSpans.count)
|
||||
duration: result.duration, selfSpanCount: result.selfSpans.count,
|
||||
visualSegmentCount: visualCount)
|
||||
lastProcess = ProcessInputs(
|
||||
folder: folder, sessionId: folder.lastPathComponent, app: currentLabel,
|
||||
mixedURL: result.mixedURL, timeline: timeline)
|
||||
@@ -387,8 +396,8 @@ final class SessionController: ObservableObject {
|
||||
stopTimer()
|
||||
let folder = currentFolder
|
||||
let result = await recorder.stop()
|
||||
let timeline = await stopVisualAndTimeline(result, folder: folder)
|
||||
finish(result, timeline: timeline)
|
||||
let visual = await stopVisualAndTimeline(result, folder: folder)
|
||||
finish(result, timeline: visual.timeline, visualRan: visual.visualRan)
|
||||
} else if lifecycleGeneration == gen {
|
||||
break // settled: no new transition was spawned
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user