Record a meeting from both sides, and write its minutes

Who said what is the hard part of a meeting transcript, and the usual answer
is to hand one mixed recording to a model and ask it to tell the voices apart.
That guess is wrong often enough to be worse than useless in minutes, where a
decision attributed to the wrong person is a decision nobody made.

So the question never reaches a model. ffmpeg records the microphone and the
default sink's monitor as one stereo stream, you on the left and everyone else
on the right, and one process reading both is what keeps them aligned over an
hour. Each channel is transcribed on its own and the two are interleaved on a
single timeline, so attribution is settled by the wire a voice arrived on.
What a microphone picks up from the speakers lands on both channels; our copy
is dropped when it overlaps theirs in time and says nearly the same thing.

The stream is written to disk as it arrives rather than held in memory, so
length costs nothing and a crash costs the tail instead of the whole meeting.
Every stage the run reaches is recorded in meetings.jsonl, so a failure while
summarising does not throw away the transcription of an hour of audio: the
retry reads the transcript back out of the document and picks up from there.
A run that dies keeps its recording whether or not audio is being kept.

The minutes model is configured on its own, under Settings, with its own
prompt, and it is told who was expected in the room so the names come out
spelled right. It is told outright that the transcript is a record of other
people talking, not instructions addressed to it.

The built-in listener now holds several bindings rather than one, and the KDE
side is parameterised by desktop id, so the meeting toggle gets a shortcut of
its own on the same footing as the dictation one.
This commit is contained in:
yusufipk
2026-07-28 16:32:31 +07:00
parent 0bdee07564
commit 352f7b6c22
11 changed files with 1783 additions and 82 deletions
+12 -1
View File
@@ -46,6 +46,7 @@ set next to it.
| --- | --- |
| Start / stop recording | `Ctrl+Space`, or click the tray icon |
| Cancel a recording | Tray menu → *Cancel recording*, or `dikte cancel` |
| Start / end a meeting | Tray menu → *Record a meeting*, or `dikte meeting` |
| Settings | Tray menu → *Settings*, or `dikte settings` |
| Reload after an update | Tray menu → *Restart*, or `dikte restart` |
| Quit | Tray menu → *Quit*, or `dikte quit` |
@@ -80,6 +81,15 @@ elapsed time, then the stage it is on. It never takes focus. Pressing
- **A failed cleanup is never silent.** The raw transcript is still pasted so the
dictation is not lost, but the indicator turns amber with the reason instead of
looking like a normal run.
- **Meetings** are recorded from the microphone and the speaker output at the
same time, which settles who said what by the channel a voice arrived on
instead of guessing at it. The two sides are transcribed separately and
interleaved into one timestamped transcript, and a second model, configured
under Settings → Meeting along with its own instruction, turns that into
minutes: decisions, action items, open questions. They land in
`~/.local/share/dikte/meetings` and in Settings → Minutes. A run that fails
keeps its recording, and a retry resumes from the transcript it already paid
for.
- **Audio and video files** run through the same models under Settings → Audio
file, optionally with `[mm:ss]` timestamps, chunked through ffmpeg when long,
and saved as `.txt` or as `.srt` subtitles.
@@ -100,7 +110,8 @@ needs your user in the `input` group: `sudo usermod -aG input $USER`.
```
dikte.py entry point, tray icon, state machine, IPC
audio.py raw PCM capture through pw-record plus the level meter
audio.py PCM capture: pw-record for dictation, ffmpeg for a meeting
meeting.py channel split, speaker labelling, cleanup, minutes
api.py transcription on either provider, OpenRouter cleanup (stdlib only)
worker.py transcribe → clean up → clipboard → paste
vad.py deciding whether a recording holds speech at all