<feed xmlns='http://www.w3.org/2005/Atom'>
<title>dotemacs/modules/transcription-config.el, branch load-graph-classify-end</title>
<subtitle>My Emacs configuration
</subtitle>
<id>https://git.cjennings.net/dotemacs/atom?h=load-graph-classify-end</id>
<link rel='self' href='https://git.cjennings.net/dotemacs/atom?h=load-graph-classify-end'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/'/>
<updated>2026-05-24T22:01:40+00:00</updated>
<entry>
<title>docs(load-graph): classify remaining domain and optional modules</title>
<updated>2026-05-24T22:01:40+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-24T22:01:40+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=f84bba186dca1543f9c5f02c03af355188b6e87c'/>
<id>urn:sha1:f84bba186dca1543f9c5f02c03af355188b6e87c</id>
<content type='text'>
Final classification batch: the last 19 modules — linear-config, local-repository, lorem-optimum, mail-config, markdown-config, music-config, pdf-config, quick-video-capture, reconcile-open-repos, restclient-config, slack-config, system-commands, telega-config, tramp-config, transcription-config, video-audio-recording, vterm-config, weather-config, wrap-up. I annotated each header, added a Batch 9 table to the inventory, and extended the validation allowlist. 101 of 102 modules are now classified; only elfeed-config remains, deferred on its test fix.

Two more hidden dependencies turned up. video-audio-recording uses the boundp shim for its C-; r binding, and mail-config registers C-; e directly without requiring keybindings, so it errors standalone rather than degrading. Both recorded for Phase 2.
</content>
</entry>
<entry>
<title>refactor(auth): consolidate the auth-source secret lookup into one helper</title>
<updated>2026-05-23T00:32:32+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-23T00:32:32+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=f6e5885b47e3ab244b293f4e478af7e520180710'/>
<id>urn:sha1:f6e5885b47e3ab244b293f4e478af7e520180710</id>
<content type='text'>
The auth-source-search + funcall-the-secret block was copied four times: calendar-sync--calendar-url, cj/auth-source-secret (ai-config), cj/--auth-source-password (transcription), and cj/slack--get-credential. Each searched authinfo, pulled :secret, and called it when the netrc backend returned a function.

I pulled that into cj/auth-source-secret-value in system-lib (a leaf, so calendar-sync doesn't have to depend on ai-config and drag in the gptel stack). It takes an optional user and returns the secret or nil. The four callers now delegate to it: ai-config layers its required-secret error on top, and the others keep their nil-on-miss behavior. With the direct auth-source-search calls gone, I dropped the now-unused (require 'auth-source) from transcription, slack, and calendar-sync. The helper's autoload covers it.

The transcription tests that exercise the delegated path stay green, and the primitive and the error wrapper get their own tests.
</content>
</entry>
<entry>
<title>feat(transcription): extend dired T to transcribe videos via ffmpeg, with tests</title>
<updated>2026-05-14T19:09:16+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-14T19:09:16+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=4a7529393ebbbc225939f21be783ec624d0a8168'/>
<id>urn:sha1:4a7529393ebbbc225939f21be783ec624d0a8168</id>
<content type='text'>
Pressing `T' in dired/dirvish on an audio file already transcribed
it; on a video file it bounced with "Not an audio file".  Real
recordings ship as .mp4 / .mkv at least as often as raw .m4a, so
the one-key flow ended at the wrong place.

Pipeline now:
- audio path -&gt; direct into `cj/--start-transcription-process'
  (unchanged).
- video path -&gt; async ffmpeg extracts the audio track to a temp
  .mp3 under `temporary-file-directory' (libmp3lame, VBR q:a 4,
  ~165kbps -- right size for speech, accepted by every backend),
  then transcribes that file with the temp marked for cleanup
  after the transcription sentinel fires.

Surface changes:
- `cj/video-file-extensions' added to user-constants.el (mp4, mkv,
  mov, webm, avi, m4v, wmv, flv, mpg, mpeg, 3gp, ogv).
- New predicates `cj/--video-file-p' / `cj/--media-file-p'.
- New `cj/--extract-audio-from-video' (async ffmpeg with success
  callback; surfaces `cj/--notify' on failure; user-errors if
  ffmpeg isn't on PATH).
- `cj/--start-transcription-process' gains optional `cleanup-file'.
  Sentinel deletes it after the existing logic runs.  Backwards
  compatible -- the audio flow doesn't pass it.
- `cj/transcribe-audio' renamed to `cj/transcribe-media' (dispatcher
  on audio vs video).  `cj/transcribe-audio-at-point' renamed to
  `cj/transcribe-media-at-point'.  Both old names kept as
  `defalias' so M-x history and any external references still work.
- `T' in dired-mode-map + dirvish-mode-map points at
  `cj/transcribe-media-at-point'.
- Module commentary USAGE block updated.

15 new ERT tests in `tests/test-transcription-video.el' cover the
predicates (happy/boundary/error), ffmpeg invocation (correct args
+ missing-ffmpeg path), the dispatcher (audio direct, video via
extraction, non-media rejected), the aliases, and the T binding.
One existing test in `test-transcription-status-and-commands.el'
updated to stub the new delegate name.

Verified locally that ffmpeg is on PATH with libmp3lame, and that
the exact arg list my code uses produces a valid MP3 from a
synthetic test video.
</content>
</entry>
<entry>
<title>refactor: clear transcription C-; T menu, move telega launcher to C-; T</title>
<updated>2026-05-14T15:47:08+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-14T15:47:08+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=c0ddf4cf68cb2cdbe1d1758a9da1080d081058b9'/>
<id>urn:sha1:c0ddf4cf68cb2cdbe1d1758a9da1080d081058b9</id>
<content type='text'>
The transcription menu wasn't earning its top-level keymap slot --
the commands (transcribe-audio, switch-backend, view-transcriptions,
kill-transcription) are run rarely enough that `M-x' is fine.  Drop
the `cj/transcribe-map' keymap, its `(keymap-set cj/custom-keymap
"T" ...)' binding, and the which-key labels.  Commands stay
callable by name.

That frees `C-; T' for telega, where the mnemonic actually fits.
Move the launcher from `C-; G' to `C-; T'.  Update the
which-key label, the module commentary, and the keymap-binding
test assertion.  The dashboard `g' single-letter binding stays put
-- `t' there is vterm, so dashboard letters and the global
`C-;' prefix don't share a key space anyway.
</content>
</entry>
<entry>
<title>refactor(transcription): extract running-transcriptions and format-entry</title>
<updated>2026-04-19T12:15:29+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-19T12:15:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=e6a87bffca9d44ec180f7a17f6891de8f4bfccec'/>
<id>urn:sha1:e6a87bffca9d44ec180f7a17f6891de8f4bfccec</id>
<content type='text'>
Two cleanups round out the transcription-config refactor:
- cj/--running-transcriptions: the 'status = running' filter used by
  cleanup and count helpers is now one function. Existing counter tests
  cover both callers.
- cj/--format-transcription-entry: the 13-line dolist body inside
  cj/transcriptions-buffer becomes a testable pure function. 6 tests
  cover status-face mapping, basename-only rendering, duration format,
  trailing newline.
</content>
</entry>
<entry>
<title>refactor(transcription): extract four sentinel side-effect helpers</title>
<updated>2026-04-19T12:12:34+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-19T12:12:34+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=7c7a1ea9da1b6020c4f0e34cc47b752f20d81ce8'/>
<id>urn:sha1:7c7a1ea9da1b6020c4f0e34cc47b752f20d81ce8</id>
<content type='text'>
Break cj/--transcription-sentinel's seven inline side-effects into named
helpers:
- cj/--write-transcript-on-success: writes process output to .txt on success
- cj/--append-to-log: appends event marker + process output to log
- cj/--update-transcription-status: marks tracking-list entry complete/error
- cj/--notify-completion: sends success or critical notification

Also: switch the tautological (cj/--should-keep-log t) to use the local
success-p (equivalent but matches the function signature), and rename
the unused audio-file sentinel arg to _audio-file.

Sentinel shrinks from 48 lines with 7 inline blocks to 14 lines of
straight-line helper calls. 10 tests cover the extracted helpers.
</content>
</entry>
<entry>
<title>refactor(transcription): extract init-log-file and track-transcription</title>
<updated>2026-04-19T12:10:17+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-19T12:10:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=e18d90875c221b489a568421e8356c1ec8fdfe85'/>
<id>urn:sha1:e18d90875c221b489a568421e8356c1ec8fdfe85</id>
<content type='text'>
Pull two more helpers out of cj/--start-transcription-process:
- cj/--init-log-file: writes the initial log header with timestamp,
  backend, audio file, script path
- cj/--track-transcription: pushes a running-status entry and refreshes
  the modeline

Start-process shrinks from 58 lines with 4 levels of nesting to ~25 lines
mostly at depth 1-2. 10 tests cover the extracted helpers.
</content>
</entry>
<entry>
<title>refactor(transcription): extract cj/--build-process-environment</title>
<updated>2026-04-19T12:08:22+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-19T12:08:22+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=13c057a75ce6d92f9de7e1d53d0ae2554261cb2e'/>
<id>urn:sha1:13c057a75ce6d92f9de7e1d53d0ae2554261cb2e</id>
<content type='text'>
Pull the per-backend env-var assembly out of cj/--start-transcription-process
into a standalone pure function. 9 tests cover: the three backends, parent-env
preservation, non-mutation, missing-key user-error, unknown-backend error.
</content>
</entry>
<entry>
<title>refactor(transcription): consolidate backends into descriptor alist</title>
<updated>2026-04-19T12:06:45+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-04-19T12:06:45+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=85d9127ea6a5cd624dd4567618ce87b12e491e8c'/>
<id>urn:sha1:85d9127ea6a5cd624dd4567618ce87b12e491e8c</id>
<content type='text'>
Introduce cj/--transcription-backends alist mapping each backend to
(:script :auth-host :env-var). Replace:
- two near-identical cj/--get-{openai,assemblyai}-api-key functions with
  a single parameterized cj/--auth-source-password helper
- the pcase in cj/--transcription-script-path with an alist lookup
- the pcase block in cj/--start-transcription-process that assembled
  the API-key env var with an alist-driven assembly

Adding a new backend is now a single line in the alist. The existing
tests plus retargeted API-key tests (now 10, covering the parameterized
helper and the descriptor data) verify no behavior change.
</content>
</entry>
<entry>
<title>fix(transcription): add T keybinding to dirvish-mode-map</title>
<updated>2026-01-29T21:59:34+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-01-29T21:59:34+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/dotemacs/commit/?id=d862331efcfed90cf28d0aa3cde00a8507a4b064'/>
<id>urn:sha1:d862331efcfed90cf28d0aa3cde00a8507a4b064</id>
<content type='text'>
Dirvish uses its own keymap rather than inheriting from dired-mode-map,
so the transcription keybinding needs to be explicitly added.
</content>
</entry>
</feed>
