diff --git a/crates/transcript/src/render.rs b/crates/transcript/src/render.rs index b1b26efa34..6bb3392014 100644 --- a/crates/transcript/src/render.rs +++ b/crates/transcript/src/render.rs @@ -71,11 +71,11 @@ pub fn render_transcript_segments( .map(|started_at| started_at - base_started_at) .unwrap_or(0); - let (words, mut assignments) = + let (words, user_assignments) = offset_transcript_data(transcript.words, transcript.assignments, offset); - let channel_assignments = + let mut assignments = channel_assignments_for_participants(&participant_human_ids, self_human_id.as_deref()); - assignments.extend(channel_assignments); + assignments.extend(user_assignments); let segments = build_segments(&words, &[], &assignments, Some(&segment_options)); all_segments.extend(segments); @@ -464,6 +464,40 @@ mod tests { assert_eq!(segments[1].text, "remote more"); } + #[test] + fn user_assignment_overrides_auto_channel_assignment() { + let segments = render_transcript_segments(RenderTranscriptRequest { + transcripts: vec![RenderTranscriptInput { + started_at: Some(0), + words: vec![ + word("w1", " hello", 0, 100, 0), + word("w2", " remote", 120, 220, 1), + ], + assignments: vec![channel_assignment("override", ChannelProfile::RemoteParty)], + }], + participant_human_ids: vec!["self".to_string(), "auto-remote".to_string()], + self_human_id: Some("self".to_string()), + humans: vec![ + RenderTranscriptHuman { + human_id: "self".to_string(), + name: "Me".to_string(), + }, + RenderTranscriptHuman { + human_id: "auto-remote".to_string(), + name: "Auto".to_string(), + }, + RenderTranscriptHuman { + human_id: "override".to_string(), + name: "Override".to_string(), + }, + ], + }); + + assert_eq!(segments.len(), 2); + assert_eq!(segments[0].speaker_label, "Me"); + assert_eq!(segments[1].speaker_label, "Override"); + } + #[test] fn keeps_missing_started_at_rows_anchored_at_zero() { let segments = render_transcript_segments(RenderTranscriptRequest { diff --git a/crates/transcript/src/segments/collect.rs b/crates/transcript/src/segments/collect.rs index deb482f75c..83b5af1b71 100644 --- a/crates/transcript/src/segments/collect.rs +++ b/crates/transcript/src/segments/collect.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use crate::types::{ChannelProfile, Segment, SegmentBuilderOptions, SegmentKey, SegmentWord}; use super::model::{ProtoSegment, ResolvedWordFrame, SpeakerIdentity, SpeakerState}; -use super::speakers::assign_complete_channel_human_id; +use super::speakers::assign_channel_human_id; pub(super) fn collect_segments( frames: Vec, @@ -44,7 +44,7 @@ pub(super) fn propagate_identity(segments: &mut Vec, speaker_state let mut last_kept_idx: Option = None; for read_index in 0..segments.len() { - assign_complete_channel_human_id(&mut segments[read_index], speaker_state); + assign_channel_human_id(&mut segments[read_index], speaker_state); let should_merge = last_kept_key.as_ref().is_some_and(|last_key| { *last_key == segments[read_index].key && segments[read_index].key.has_speaker_identity() diff --git a/crates/transcript/src/segments/speakers.rs b/crates/transcript/src/segments/speakers.rs index 7bb6d7fea9..e3d7493681 100644 --- a/crates/transcript/src/segments/speakers.rs +++ b/crates/transcript/src/segments/speakers.rs @@ -85,15 +85,12 @@ pub(super) fn resolve_identities( .collect() } -pub(super) fn assign_complete_channel_human_id(segment: &mut ProtoSegment, state: &SpeakerState) { +pub(super) fn assign_channel_human_id(segment: &mut ProtoSegment, state: &SpeakerState) { if segment.key.speaker_human_id.is_some() { return; } let channel = segment.key.channel; - if !state.complete_channels.contains(&channel) { - return; - } if let Some(human_id) = state.human_id_by_channel.get(&channel) { segment.key = SegmentKey { @@ -120,7 +117,6 @@ fn apply_identity_rules( } if identity.human_id.is_none() - && state.complete_channels.contains(&word.channel) && let Some(human_id) = state.human_id_by_channel.get(&word.channel) { identity.human_id = Some(human_id.clone()); diff --git a/crates/transcript/src/segments/tests.rs b/crates/transcript/src/segments/tests.rs index aa97062e2d..21c59fdf55 100644 --- a/crates/transcript/src/segments/tests.rs +++ b/crates/transcript/src/segments/tests.rs @@ -405,6 +405,16 @@ fn propagates_remote_party_identity_when_channel_marked_complete() { assert_eq!(result[0].key.speaker_human_id.as_deref(), Some("remote")); } +#[test] +fn applies_channel_assignment_even_without_complete_channel() { + let finals = vec![fw("0", 0, 100, 1), fw("1", 200, 300, 1)]; + let assignments = vec![channel_human("remote", ChannelProfile::RemoteParty)]; + // Default options only mark DirectMic as complete, not RemoteParty + let result = build_segments(&finals, &[], &assignments, None); + assert_eq!(result.len(), 1); + assert_eq!(result[0].key.speaker_human_id.as_deref(), Some("remote")); +} + #[test] fn partial_word_ignores_its_own_runtime_hint_and_keeps_previous_segment_key() { let finals = vec![fw_si("0", 0, 100, 0, 0)];