Skip to content

feat: allow speaker labels for video files - #1324

Draft
tarushvkodes wants to merge 1 commit into
altic-dev:mainfrom
tarushvkodes:feat/speaker-labels-video-files
Draft

tarushvkodes wants to merge 1 commit into
altic-dev:mainfrom
tarushvkodes:feat/speaker-labels-video-files

Conversation

@tarushvkodes

Copy link
Copy Markdown

Description

Lets File Transcription label speakers in video files (.mov, .mp4 and other UTType.movie types), not only audio files.

Why video shouldn't affect speaker ID

Speaker identification never looks at the container. It only sees decoded audio samples:

  1. The diarizer opens the file in FluidAudio's StreamingAudioSourceFactory.makeDiskBackedSource, which calls AVAudioFile(forReading:).
  2. The per-speaker transcription step (transcribeFileWithSpeakerLabels) opens the file again with AVAudioFile(forReading:) to slice each turn.

AVAudioFile reads only the audio track of a movie file and ignores the video track. The standard (non-speaker) path in the same function already depends on that: for a video container it skips transcribeFile(at:) and uses AVAudioFile(forReading:) with chunked reads. So every file read in the speaker path has a working equivalent for video today. The !isVideoContainer check was the only thing turning it off.

If the audio track is missing or unreadable, transcribeFileWithSpeakerLabels returns nil and the existing fallback runs. The user gets an unlabeled transcript and the existing "Speaker labeling was unavailable for this file" notice, the same as for a bad audio file.

Changes

  • FileTranscriptionService no longer skips diarization for video containers, and its "skipped for video container" log branch is gone. isVideoContainer still routes video away from the provider's native transcribeFile(at:) path, unchanged.
  • FileTranscriptionView no longer disables the "Label speakers" toggle or hides the speaker-count picker for video, and drops the "Available for audio files only" caption.

Type of Change

  • 🐞 Bug fix
  • ✨ New feature
  • 💥 Breaking change
  • 🧹 Chore
  • 📝 Documentation update

Related Issue or Discussion

Closes #1322

This change turns on a toggle that already exists for video files. It adds no new UI.

Testing

Same samples from video and audio. I took each video and copied its audio track into an .m4a without re-encoding (ffmpeg -i in.mov -vn -c:a copy out.m4a). Then I decoded both files with AVAudioFile(forReading:), the call both speaker-path reads use, and hashed the float samples:

ab0c48cc4b99e378 1506206 dialog.mov   (H.264 + AAC, two synthetic voices, 33 s)
ab0c48cc4b99e378 1506206 dialog.m4a
61142be83ed507ab 14400512 clip.mov    (HEVC + AAC screen recording, 5 min)
61142be83ed507ab 14400512 clip.m4a

The hashes and frame counts match, so the diarizer gets the same input either way.

Diarizer output. I ran OfflineDiarizerManager(config: .default).process(url) from FluidAudio at the pinned revision 386f719. On the 5-minute recording, the .mov and the .m4a gave identical segments: 8 segments, the same start and end times, one speaker. With withSpeakers(exactly: 2) the results vary from run to run on both the .mov and the .m4a, so that variation comes from clustering, not from the container.

Build. ./build.sh unsigned gives ** BUILD SUCCEEDED **.

Manual check for reviewers. Choose a .mov with two speakers in File Transcription. "Label speakers" is now enabled. Turn it on and transcribe. The result shows Speaker 1 / Speaker 2 segments, like the same audio saved as .m4a.

  • Tested on Intel Mac
  • Tested on Apple Silicon Mac
  • Tested on macOS version: 27.2.1 (M5 Pro, arm64), FluidVoice main @ 3fb8b70
  • Ran linter locally: swiftlint --strict --config .swiftlint.yml on the changed files, 0 violations
  • Ran formatter locally: SwiftFormat 0.64.6 flags both files, but only on untouched lines that main flags too (repeatedGuard, wrapIfStatementBodies, docComments). Nothing on the changed lines.
  • Ran tests locally: not run for this change. It removes two conditions and adds no new logic to test.

Not done: a full end-to-end run of the patched app on a video, and Intel (the speaker toggle is hidden there anyway because SpeakerDiarizationService.isSupported is false).

Screenshots / Video

The sample-hash and diarizer output above are the evidence. A screenshot of the enabled toggle for a .mov still needs to be added.

Notes

AI use: Claude Code traced the gate, wrote the probe that compares the diarizer's input and output for video and audio files, and drafted the diff and this description.

  • A human created this PR or reviewed and approved it before submission, understands the full diff, and takes responsibility for the change.

🤖 Generated with Claude Code

File transcription skipped diarization whenever the file extension
conformed to UTType.movie, and the UI disabled the "Label speakers"
toggle for video. The diarizer (FluidAudio StreamingAudioSourceFactory)
and the per-turn slicer both read the file through
AVAudioFile(forReading:), which decodes only the audio track. The
standard buffered path already relies on that same call for video
containers, so the speaker path needs no special case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the needs screenshots Pull request needs screenshot or video evidence. label Nov 13, 2026
@github-actions

Copy link
Copy Markdown

The PR Policy check is blocking this PR because required template information is missing.

Please update the PR description with:

  • Screenshots / Video

Visual files detected:

  • Sources/Fluid/UI/FileTranscriptionView.swift

Screenshots or video are required for UI, UX, settings, onboarding, overlay, menu bar, or visual behavior changes. If this PR has no visual changes, check the no-visual-change box in the template.

If this remains incomplete for 48 hours after opening, the PR may be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs screenshots Pull request needs screenshot or video evidence.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

File Transcription: allow speaker labels for video files

1 participant