usb-audio-transcriber 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. usb_audio_transcriber-1.1.0/.github/pull_request_template.md +21 -0
  2. usb_audio_transcriber-1.1.0/.gitignore +35 -0
  3. usb_audio_transcriber-1.1.0/CHANGELOG.md +56 -0
  4. usb_audio_transcriber-1.1.0/CONTRIBUTING.md +41 -0
  5. usb_audio_transcriber-1.1.0/LICENSE +21 -0
  6. usb_audio_transcriber-1.1.0/PKG-INFO +58 -0
  7. usb_audio_transcriber-1.1.0/README.md +263 -0
  8. usb_audio_transcriber-1.1.0/SECURITY.md +26 -0
  9. usb_audio_transcriber-1.1.0/bin/app.py +1307 -0
  10. usb_audio_transcriber-1.1.0/bin/benchmark-models.py +102 -0
  11. usb_audio_transcriber-1.1.0/bin/diarize.py +138 -0
  12. usb_audio_transcriber-1.1.0/bin/doctor.py +311 -0
  13. usb_audio_transcriber-1.1.0/bin/ingest.py +325 -0
  14. usb_audio_transcriber-1.1.0/bin/llm.py +165 -0
  15. usb_audio_transcriber-1.1.0/bin/model-cache.py +55 -0
  16. usb_audio_transcriber-1.1.0/bin/model_profiles.py +114 -0
  17. usb_audio_transcriber-1.1.0/bin/notify.py +112 -0
  18. usb_audio_transcriber-1.1.0/bin/panel.py +928 -0
  19. usb_audio_transcriber-1.1.0/bin/pipeline_config.py +81 -0
  20. usb_audio_transcriber-1.1.0/bin/progress-popup.py +113 -0
  21. usb_audio_transcriber-1.1.0/bin/run-cycle.sh +26 -0
  22. usb_audio_transcriber-1.1.0/bin/search.py +233 -0
  23. usb_audio_transcriber-1.1.0/bin/sessions.py +486 -0
  24. usb_audio_transcriber-1.1.0/bin/setup.py +316 -0
  25. usb_audio_transcriber-1.1.0/bin/transcribe.py +315 -0
  26. usb_audio_transcriber-1.1.0/bootstrap.sh +34 -0
  27. usb_audio_transcriber-1.1.0/config.example.env +112 -0
  28. usb_audio_transcriber-1.1.0/docs/README.md +35 -0
  29. usb_audio_transcriber-1.1.0/docs/architecture.md +73 -0
  30. usb_audio_transcriber-1.1.0/docs/assets/how-it-works.svg +63 -0
  31. usb_audio_transcriber-1.1.0/docs/configuration.md +152 -0
  32. usb_audio_transcriber-1.1.0/docs/development.md +101 -0
  33. usb_audio_transcriber-1.1.0/docs/model-profiles.md +54 -0
  34. usb_audio_transcriber-1.1.0/docs/obsidian.md +45 -0
  35. usb_audio_transcriber-1.1.0/docs/panel.md +51 -0
  36. usb_audio_transcriber-1.1.0/docs/privacy-and-security.md +50 -0
  37. usb_audio_transcriber-1.1.0/docs/pypi-readme.md +29 -0
  38. usb_audio_transcriber-1.1.0/docs/troubleshooting.md +159 -0
  39. usb_audio_transcriber-1.1.0/docs/usage.md +212 -0
  40. usb_audio_transcriber-1.1.0/install.sh +142 -0
  41. usb_audio_transcriber-1.1.0/panel/index.html +516 -0
  42. usb_audio_transcriber-1.1.0/prompts/session-summary.md +22 -0
  43. usb_audio_transcriber-1.1.0/pyproject.toml +74 -0
  44. usb_audio_transcriber-1.1.0/requirements-diarization.txt +3 -0
  45. usb_audio_transcriber-1.1.0/requirements.txt +2 -0
  46. usb_audio_transcriber-1.1.0/share/io.github.aaacharlie.UsbAudioTranscriber.desktop +18 -0
  47. usb_audio_transcriber-1.1.0/share/io.github.aaacharlie.UsbAudioTranscriber.svg +8 -0
  48. usb_audio_transcriber-1.1.0/systemd/usb-audio-transcriber-panel.service +12 -0
  49. usb_audio_transcriber-1.1.0/systemd/usb-audio-transcriber-plug.path +13 -0
  50. usb_audio_transcriber-1.1.0/systemd/usb-audio-transcriber-plug.service +12 -0
  51. usb_audio_transcriber-1.1.0/systemd/usb-audio-transcriber.service +9 -0
  52. usb_audio_transcriber-1.1.0/systemd/usb-audio-transcriber.timer +11 -0
  53. usb_audio_transcriber-1.1.0/tests/test_app.py +176 -0
  54. usb_audio_transcriber-1.1.0/tests/test_benchmark.py +46 -0
  55. usb_audio_transcriber-1.1.0/tests/test_bootstrap.py +106 -0
  56. usb_audio_transcriber-1.1.0/tests/test_cli.py +275 -0
  57. usb_audio_transcriber-1.1.0/tests/test_config.py +57 -0
  58. usb_audio_transcriber-1.1.0/tests/test_diarize.py +197 -0
  59. usb_audio_transcriber-1.1.0/tests/test_doctor.py +408 -0
  60. usb_audio_transcriber-1.1.0/tests/test_ingest.py +562 -0
  61. usb_audio_transcriber-1.1.0/tests/test_install.py +314 -0
  62. usb_audio_transcriber-1.1.0/tests/test_llm.py +145 -0
  63. usb_audio_transcriber-1.1.0/tests/test_notify.py +202 -0
  64. usb_audio_transcriber-1.1.0/tests/test_panel.py +421 -0
  65. usb_audio_transcriber-1.1.0/tests/test_progress.py +686 -0
  66. usb_audio_transcriber-1.1.0/tests/test_release_notes.py +57 -0
  67. usb_audio_transcriber-1.1.0/tests/test_run_cycle.py +270 -0
  68. usb_audio_transcriber-1.1.0/tests/test_search.py +217 -0
  69. usb_audio_transcriber-1.1.0/tests/test_sessions.py +497 -0
  70. usb_audio_transcriber-1.1.0/tests/test_setup.py +327 -0
  71. usb_audio_transcriber-1.1.0/uninstall.sh +26 -0
  72. usb_audio_transcriber-1.1.0/usb_audio_transcriber/__init__.py +14 -0
  73. usb_audio_transcriber-1.1.0/usb_audio_transcriber/cli.py +406 -0
@@ -0,0 +1,21 @@
1
+ ## Summary
2
+
3
+ -
4
+
5
+ ## Safety and privacy
6
+
7
+ - Source deletion behavior changed: no
8
+ - Cloud data flow changed: no
9
+ - Credential handling changed: no
10
+ - Existing `config.env` compatibility changed: no
11
+
12
+ ## Verification
13
+
14
+ - [ ] `python3 -m py_compile bin/*.py usb_audio_transcriber/*.py`
15
+ - [ ] `bash -n install.sh uninstall.sh bootstrap.sh bin/run-cycle.sh`
16
+ - [ ] `python3 -m unittest discover -s tests -v`
17
+ - [ ] `git diff --check`
18
+
19
+ ## Manual testing
20
+
21
+ Describe any USB, desktop, model, or real-audio validation. Do not attach private recordings or transcripts.
@@ -0,0 +1,35 @@
1
+ # Local configuration and credentials
2
+ config.env
3
+ config.env.*
4
+ !config.example.env
5
+
6
+ # Python environments, caches, and package builds
7
+ venv/
8
+ __pycache__/
9
+ *.py[cod]
10
+ dist/
11
+ build/
12
+ *.egg-info/
13
+
14
+ # Runtime data: recordings, transcripts, logs, progress, and deduplication state
15
+ var/
16
+ archive/
17
+ queue/
18
+ vault/
19
+ transcripts/
20
+ *.sqlite
21
+ *.sqlite-*
22
+
23
+ # Imported media and generated transcript artifacts
24
+ *.wav
25
+ *.mp3
26
+ *.m4a
27
+ *.json
28
+ *.txt
29
+ !requirements.txt
30
+ !requirements-diarization.txt
31
+
32
+ # Editor and OS artifacts
33
+ .DS_Store
34
+ .vscode/
35
+ .idea/
@@ -0,0 +1,56 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow [Semantic Versioning](https://semver.org/).
4
+
5
+ ## [Unreleased]
6
+
7
+ Nothing yet.
8
+
9
+ ## [1.1.0] - 2026-09-06
10
+
11
+ The control panel becomes a real desktop window, summaries can use a tool you already pay for, and the program is a pipx package.
12
+
13
+ ### Added
14
+
15
+ - The control panel is a desktop window: `bin/app.py`, a native GTK 4 / libadwaita app with the same pages as the web panel (home, sessions, recordings, search, settings, tools), talking to the panel server's local API. The app-menu entry and `panel.py open` start it when the GTK bindings are installed (`python3-gi gir1.2-gtk-4.0 gir1.2-adw-1`) and fall back to the web page otherwise; `--page` opens on a given page, `--web` and `--browser` choose the web page. `GET /api/link` returns the private link for another device.
16
+ - pipx and PyPI packaging (#10): `pyproject.toml` builds a wheel whose `usb-audio-transcriber` command wraps every script (`install`, `uninstall`, `update`, `cycle`, `panel`, `doctor`, `setup`, `sessions`, `search`, `model-cache`, `benchmark`, `paths`). `usb-audio-transcriber install` writes the same units and menu entry as `install.sh`, against the same `~/.local/share/usb-audio-transcriber/config.env`, so the two install paths are interchangeable. The version is the git tag, CI installs the built wheel with pipx, and the Release workflow gains a PyPI `publish` job that runs once trusted publishing is configured. `USB_AUDIO_TRANSCRIBER_ROOT` moves the data folder; `install.sh` and `bootstrap.sh` are unchanged for users.
17
+ - The control panel (`bin/panel.py`, an app-menu entry, and `usb-audio-transcriber-panel.service`): a token-protected local web app with the pipeline's state, sessions with a Summarize button and backend picker, recordings, search, every setting as a validated form (with "Find my Obsidian vault"), and tools for the doctor, search index, model cache, session rebuilds, and the log. `sessions.py summarize --id` and `--backend` back its actions from the terminal. Every button shows its result, streamed while it runs, in an Activity box on the page where it was pressed, and the panel opens as its own window when Chrome, Chromium, Brave, or Edge is installed (`panel.py open --browser` forces a tab).
18
+ - Summary backends (`SUMMARY_BACKEND`): summaries can now come from a command-line AI tool you already pay for (`command`: Codex, Claude Code, Gemini CLI, your own agent), any OpenAI-compatible server such as a local Ollama (`openai`), or OpenRouter (`openrouter`). The setup wizard asks which, `sessions.py test-backend` checks it, and empty keeps the old OpenRouter-if-key behaviour.
19
+ - `SESSION_BACKFILL_DAYS` (default 7): on an installation with history, sessions that ended more than that many days ago get notes without an automatic AI summary, and a cycle that writes more than three session notes sends one notification instead of one per note. `sessions.py retry` summarizes older sessions on demand.
20
+ - Full-text search across all transcripts (`bin/search.py`): an FTS5 index in the state database, refreshed every cycle, with `--since`, `--speaker`, prefix, `--raw`, and `--json` options. (#11)
21
+ - `WHISPER_TASK="translate"` translates speech in other languages straight into English instead of transcribing it; the task is validated by the doctor and recorded in each note's front matter (#13, contributed by @anni-x1).
22
+
23
+ ### Fixed
24
+
25
+ - The desktop window keeps one instance per session (a second launch raises the window), carries the app icon in the dock (the menu entry and icon are now named after the window's id, `io.github.aaacharlie.UsbAudioTranscriber`), and got a face: a status card on Home with the icon, the pipeline's state in one line, a progress bar while transcribing, and icons on every row; page titles use the desktop's accent colour.
26
+ - Clicking the app while its window is buried now raises the window. The menu entry asks the desktop for an activation token (`StartupNotify=true`), `panel.py open` becomes the window process instead of spawning it, and the window carries its id as its X11 class, so the desktop treats the click as permission to take focus rather than showing "USB Audio Transcriber is ready".
27
+ - The menu entry names the icon by its full path, the installer bumps the icon theme folder and refreshes a stale `icon-theme.cache`, so the microphone shows in the menu and the dock right after an update; a leftover cache had turned it into a gear.
28
+ - Updating restarts a running panel server (`systemctl --user try-restart`), so the new code is what the window talks to straight away.
29
+ - The panel's status no longer walks every mounted drive on every refresh: the "on the recorder" count is refreshed in the background at most every 30 seconds, `bin/ingest.py` skips hidden and system folders and looks at most four levels deep for the recorder's folder, and the window never stacks status requests, keeps the last good values, and reports an outage only after two failures in a row. Before, an external drive with many files made `/api/status` time out ("Panel unreachable") and kept the disk busy.
30
+
31
+ ## [1.0.0] - 2026-09-05
32
+
33
+ First tagged release. Everything below is new relative to the initial public code.
34
+
35
+ ### Added
36
+
37
+ - One-command installer (`bootstrap.sh`) that clones or updates the source and runs `install.sh`.
38
+ - First-run setup wizard (`bin/setup.py`) that finds Obsidian vaults (native, Flatpak, and Snap installs) and writes `VAULT_DIR`, the summary subject, and an optional OpenRouter key into `config.env` without touching anything else.
39
+ - Session notes (`bin/sessions.py`): recordings less than `SESSION_GAP_MIN` minutes apart become one note with `[[wikilinks]]` to every transcript and a combined transcript in order; with a key, an AI summary generated from `prompts/session-summary.md`. `list`, `retry`, and `rebuild --date` commands.
40
+ - Plug-in trigger: a systemd user path unit starts a cycle seconds after a drive mounts; `run-cycle.sh --wait` waits for a running cycle instead of skipping.
41
+ - Folder watching (`WATCH_DIRS`): recursive scanning of synced or shared folders; sources found there are never deleted.
42
+ - Desktop notifications with click-to-open (`NOTIFY`), including a failure notice.
43
+ - Headless mode (`HEADLESS`): the progress window is skipped without a display, `zenity` is optional, and the doctor warns when user lingering is off.
44
+ - Optional speaker labels with pyannote.audio (`DIARIZATION`, `./install.sh --with-diarization`).
45
+ - `FILE_SUMMARY` switch for per-recording summaries, and a shared OpenRouter client in `bin/llm.py`.
46
+ - Documentation: Obsidian guide, session notes, headless and Raspberry Pi setup, speaker labels, a README rewrite with the recorder test and the summary workflow, and hero and social preview images.
47
+ - Release workflow (`.github/workflows/release.yml`) and this changelog.
48
+
49
+ ### Changed
50
+
51
+ - `zenity` is optional at install time; missing desktop tools are warnings in the doctor.
52
+ - The doctor reports the plug-in trigger and validates every new setting.
53
+
54
+ ### Fixed
55
+
56
+ - Every successful run was recorded as "Transcription failed" in the progress state because the catch-all handler also caught `SystemExit`, so the desktop window could show the failure text at the end of a good run.
@@ -0,0 +1,41 @@
1
+ # Contributing
2
+
3
+ Contributions are welcome when they preserve the project's local-first and source-safe behavior.
4
+
5
+ ## Before opening a change
6
+
7
+ 1. Search existing issues and pull requests.
8
+ 2. Keep the change focused; avoid unrelated refactors.
9
+ 3. Do not include recordings, transcripts, API keys, local configuration, model weights, caches, or runtime state.
10
+ 4. For destructive or privacy-affecting behavior, explain the safety boundary explicitly.
11
+
12
+ ## Development workflow
13
+
14
+ ```bash
15
+ git switch -c fix/short-description
16
+ # make the change
17
+ python3 -m py_compile bin/*.py usb_audio_transcriber/*.py
18
+ bash -n install.sh uninstall.sh bootstrap.sh bin/run-cycle.sh
19
+ python3 -m unittest discover -s tests -v
20
+ git diff --check
21
+ ```
22
+
23
+ Use a conventional commit subject such as `fix: preserve queue item after model failure` or `docs: clarify recorder discovery`.
24
+
25
+ ## Tests
26
+
27
+ Behavior changes should include deterministic tests under `tests/`. Mock expensive model execution and external APIs. Never make CI depend on private audio, API keys, model downloads, desktop access, or a mounted USB device.
28
+
29
+ If you perform a real-audio test, report hardware, model, device/compute configuration, audio duration, and elapsed time. Do not publish the source or transcript unless you have permission.
30
+
31
+ ## Pull requests
32
+
33
+ Include:
34
+
35
+ - what changed and why
36
+ - safety/privacy implications
37
+ - compatibility implications for existing `config.env` files
38
+ - exact verification commands and results
39
+ - screenshots only when the desktop UI changed and the image contains no private information
40
+
41
+ By contributing, you agree that your contribution is licensed under the repository's MIT license.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 aaacharlie
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,58 @@
1
+ Metadata-Version: 2.5
2
+ Name: usb-audio-transcriber
3
+ Version: 1.1.0
4
+ Summary: Plug in a voice recorder, walk away, come back to timestamped, searchable notes and a session summary. Local faster-whisper pipeline for Linux.
5
+ Project-URL: Homepage, https://github.com/aaacharlie/usb-audio-transcriber
6
+ Project-URL: Documentation, https://github.com/aaacharlie/usb-audio-transcriber/blob/main/docs/README.md
7
+ Project-URL: Changelog, https://github.com/aaacharlie/usb-audio-transcriber/blob/main/CHANGELOG.md
8
+ Project-URL: Issues, https://github.com/aaacharlie/usb-audio-transcriber/issues
9
+ Author: aaacharlie
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: faster-whisper,obsidian,speech-to-text,systemd,transcription,usb,voice-recorder,whisper
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Environment :: X11 Applications
16
+ Classifier: Intended Audience :: End Users/Desktop
17
+ Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
21
+ Classifier: Topic :: Office/Business
22
+ Classifier: Topic :: Text Processing
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: faster-whisper<2,>=1.2.1
25
+ Requires-Dist: requests>=2.31
26
+ Provides-Extra: diarization
27
+ Requires-Dist: pyannote-audio<4,>=3.1; extra == 'diarization'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # USB Audio Transcriber
31
+
32
+ **Plug in a voice recorder. Walk away. Come back to timestamped, searchable notes and a summary of the whole session.**
33
+
34
+ A free, open-source Linux utility that turns a cheap USB voice recorder (or any synced folder of voice memos) into a hands-off note-taking system. Plug the recorder in and, within seconds, it finds the new recordings, copies them to a checksum-verified archive, transcribes them locally with faster-whisper, and writes timestamped Markdown notes straight into your Obsidian vault or any notes folder. Recordings that a voice-activated recorder split into a dozen files are stitched back into one session note in the right order, with an optional AI summary from a tool you already pay for (Codex, Claude Code), a local Ollama model, or OpenRouter.
35
+
36
+ Nothing is uploaded: transcription runs on your own CPU, no GPU required.
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ sudo apt install pipx ffmpeg zenity libnotify-bin # Debian/Ubuntu names
42
+ pipx install usb-audio-transcriber
43
+ usb-audio-transcriber install
44
+ ```
45
+
46
+ `install` creates `~/.local/share/usb-audio-transcriber/config.env`, asks where your notes should go (it finds your Obsidian vaults), enables the background service and the plug-in trigger, and adds **USB Audio Transcriber** to your app menu: a control panel with the pipeline's state, sessions with a Summarize button, search, and every setting as a form. Everything in the panel is also a command:
47
+
48
+ ```bash
49
+ usb-audio-transcriber doctor # check the installation
50
+ usb-audio-transcriber sessions list # session notes
51
+ usb-audio-transcriber search roof leak # every matching moment, newest first
52
+ usb-audio-transcriber panel open # the control panel
53
+ usb-audio-transcriber update # pipx upgrade, then install again
54
+ ```
55
+
56
+ Speaker labels need the optional extra: `pipx install "usb-audio-transcriber[diarization]"`.
57
+
58
+ Full documentation, the recorder that was tested, and the summary workflow: <https://github.com/aaacharlie/usb-audio-transcriber>.
@@ -0,0 +1,263 @@
1
+ # USB Audio Transcriber
2
+
3
+ [![CI](https://github.com/aaacharlie/usb-audio-transcriber/actions/workflows/ci.yml/badge.svg)](https://github.com/aaacharlie/usb-audio-transcriber/actions/workflows/ci.yml)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
+ [![Platform: Linux](https://img.shields.io/badge/platform-Linux-informational.svg)](#requirements)
6
+
7
+ **Plug in a voice recorder. Walk away. Come back to timestamped, searchable notes and a summary of the whole session.**
8
+
9
+ <p align="center">
10
+ <img src="docs/assets/how-it-works.svg" alt="Step 1: record with any USB voice recorder, phone, or synced folder. Step 2: plug it in; Whisper transcribes on your own CPU seconds after the drive mounts, nothing is uploaded. Step 3: timestamped notes and session summaries land in your Obsidian vault." width="100%">
11
+ </p>
12
+
13
+ USB Audio Transcriber is a free, open-source Linux utility that turns a cheap voice recorder into a hands-off note-taking system. Plug the recorder in and, within seconds, it finds the new recordings, copies them to a checksum-verified archive, transcribes them locally with faster-whisper, and writes timestamped Markdown notes straight into your Obsidian vault or any notes folder. When a voice-activated recorder has split a meeting or a class into a dozen files, it stitches them back into one session note in the right order and, if you want, has an AI write the executive summary.
14
+
15
+ There is no subscription, no per-minute pricing, and no uploading of your audio. Transcription runs on your own CPU with no GPU required, the source recordings stay on the device, and nothing leaves your machine unless you explicitly turn on the optional cloud summary.
16
+
17
+ ## Quick start
18
+
19
+ On Debian or Ubuntu (other distributions: the same packages under their own names):
20
+
21
+ ```bash
22
+ sudo apt install python3-venv ffmpeg zenity libnotify-bin git
23
+ curl -fsSL https://raw.githubusercontent.com/aaacharlie/usb-audio-transcriber/main/bootstrap.sh | bash
24
+ ```
25
+
26
+ The installer creates a private virtual environment, asks where your notes should go (it finds your Obsidian vaults for you), and enables the background service. Then plug in the recorder. Run the same line again later to update.
27
+
28
+ Prefer pipx? The same program is a Python package with a `usb-audio-transcriber` command:
29
+
30
+ ```bash
31
+ sudo apt install pipx ffmpeg zenity libnotify-bin
32
+ pipx install git+https://github.com/aaacharlie/usb-audio-transcriber # pipx install usb-audio-transcriber once it is on PyPI
33
+ usb-audio-transcriber install
34
+ ```
35
+
36
+ Prefer to read the code first? Clone the repository and run `./install.sh`; see [Install](#install).
37
+
38
+ ## What you get
39
+
40
+ - **Zero-touch workflow.** A systemd user service watches for the recorder. A cycle starts the moment the drive mounts, with a one-minute timer as the safety net. You never run a command after setup.
41
+ - **Session notes with an executive summary.** Recordings less than 20 minutes apart become one session note: links to every transcript, one combined transcript in order, and an AI summary written from a prompt you can edit. The stitching works offline; the summary can use a tool you already pay for (Codex, Claude Code), a local Ollama model, or OpenRouter.
42
+ - **Any folder, not only USB.** Point `WATCH_DIRS` at a Syncthing, Nextcloud, or Dropbox folder and phone voice memos are transcribed too. Files there are never deleted.
43
+ - **Built for Obsidian.** The setup wizard finds your vault. Notes carry YAML front matter and tags, session notes use `[[wikilinks]]`, so backlinks and the graph view just work, and Obsidian Sync or Syncthing carry everything to your phone.
44
+ - **Private by default.** Audio never leaves your computer. Optional summaries send transcript text only, and only to the backend you choose; with Ollama even that stays local.
45
+ - **Never loses a recording.** Every copy is SHA-256 verified before it enters the queue, duplicates are detected by content rather than filename, and the USB source is never deleted unless you opt in.
46
+ - **Speaker labels, optionally.** Turn on pyannote diarization for `Speaker 1` / `Speaker 2` labels, computed locally.
47
+ - **Desktop-friendly.** A progress window with a time estimate, and a notification you can click to open the finished note.
48
+ - **Runs headless.** No desktop? Put it on a Raspberry Pi or a home server and let the notes land in a synced folder.
49
+ - **Search everything from the terminal.** `search.py roof leak` finds every matching moment across all your recordings, newest first, with the timestamp and speaker.
50
+ - **A control panel.** A real desktop window (GTK 4) from your app menu, and a web page for your phone if you like, with the pipeline's state, sessions with a Summarize button, search, and every setting as a form. Everything in it is also a terminal command.
51
+ - **Pick your speed.** `fast` transcribed a 58-minute recording in about 17 minutes on a plain CPU. `accurate` is there for hard audio, and `both` gives you an A/B comparison from the same file.
52
+
53
+ Good fits: lectures and classes, meetings and site visits, interviews, long phone calls on speaker, and voice memos you would otherwise never listen to again.
54
+
55
+ ## The control panel
56
+
57
+ <p align="center">
58
+ <img src="docs/assets/panel-home.png" alt="The control panel window on its Home page: timer and plug-in trigger state, last activity, queue, library counts, and buttons to run a cycle or pause automatic runs" width="100%">
59
+ </p>
60
+
61
+ Install adds a **USB Audio Transcriber** entry to your app menu. It opens the control panel, a native desktop window (GTK 4 / libadwaita; the same thing is also a web page for your phone or a machine without the GTK bindings): what the pipeline is doing right now, every session with a Summarize button that sends it to the AI tool you choose, search across everything, and every setting as a form with a "Find my Obsidian vault" button. It runs on your machine behind a private link, and each button maps to a script you could run yourself. Details in [the panel guide](docs/panel.md).
62
+
63
+ ## Real-world test: a $50 recorder from Amazon
64
+
65
+ This project was built around, and tested with, an inexpensive magnetic voice-activated recorder that sells on Amazon for about $50. The listing is titled "136GB(9800H) Magnetic Voice Recorder - Zutiifeu Voice Activated Recorder with DSP5.0 Noise Cancellation HD Recording Device for Classe/Meeting/Lecture". It has been used with this pipeline with lots of success, and the combination is the whole point of the project: a budget recorder plus a Linux box you already own gives you a complete recording-to-notes system.
66
+
67
+ What it looks like in practice:
68
+
69
+ 1. Record with the device. Voice activation means it can sit for hours and only capture the parts where someone is talking.
70
+ 2. Plug it into your Linux machine's USB port. The recorder mounts like an ordinary flash drive and a cycle starts within seconds.
71
+ 3. The progress window shows what was found and how long it expects to take. When it is done, a notification appears; click it to open the notes.
72
+ 4. Open your vault. Every recording has a dated, timestamped transcript note, and the whole sitting has a session note that ties them together.
73
+
74
+ Any recorder, phone, or SD card that mounts as a drive and saves into a folder should work the same way. Point `RECORDER_DIR` at the folder your device saves into (the default is `RECORD`) and add your device's file extension to `AUDIO_EXTS` if it is not `mp3`, `wav`, or `m4a`. Recorders other people have tried are collected in the repository's Discussions; please add yours.
75
+
76
+ This project has no affiliation with the recorder's manufacturer or with Amazon. It is simply the hardware the pipeline was tested on.
77
+
78
+ ## The transcript looks rough? Don't be discouraged
79
+
80
+ Raw Whisper output from a pocket recorder can look underwhelming at first glance: no paragraphs, misheard names, a sentence that trails off where the voice activation paused, and long sessions split across several files. That is normal, and it is not the finished product.
81
+
82
+ The raw transcript is the input to the last step. Give the transcripts of a session to a current frontier model (tested with GPT 5.6 Sol) and ask it to put them in order and summarize them. The model reads straight through the transcription noise, reconstructs the flow of the conversation, and returns a very high quality executive summary of the whole session.
83
+
84
+ The pipeline can do this for you, and it does not need a pay-per-token bill: point `SUMMARY_BACKEND` at a command-line AI tool you already subscribe to (Codex with a ChatGPT plan, Claude Code, Gemini CLI), at a local Ollama model, or at OpenRouter, and tell it what your recordings are about in `SESSION_SUBJECT`. The setup wizard asks exactly these questions and can be re-run any time. Every session note then opens with the summary, generated from [`prompts/session-summary.md`](prompts/session-summary.md), which you can edit.
85
+
86
+ To do it by hand instead, or with a model of your own choosing:
87
+
88
+ 1. Open the session note in your vault. Its `## Combined transcript` section already has every recording in order. (Without session notes, collect the transcript notes for the sitting; their filenames start with the recording's date and time, so the order is in the names.)
89
+ 2. Paste it into one chat with the model.
90
+ 3. Use a prompt like this one, replacing the bracketed part with the topic of the recording:
91
+
92
+ ```text
93
+ You are the world's best transcript reader and interpreter, and you are very
94
+ knowledgeable in [insert subject matter]. Can you take all of these audio
95
+ transcript files and put them in the correct order and summarize it completely?
96
+ Make it an amazing, coherent transcript summary in the proper order. Make it
97
+ something better than other AI services like Otter.
98
+ ```
99
+
100
+ Tips that make the summary better:
101
+
102
+ - Name the subject matter precisely ("commercial real estate financing", "organic chemistry lecture", "quarterly planning meeting"). It helps the model fix misheard jargon and names.
103
+ - Turn on speaker labels, or tell the model who was in the room, so it can attribute what was said.
104
+ - Ask for decisions, action items, and open questions as separate sections if you want a meeting-style report. The bundled prompt already does.
105
+ - Use a model with a large context window so a whole day's notes fit in one request.
106
+
107
+ Either way, remember that pasting a transcript into a cloud AI sends its text to that provider. Keep sensitive recordings local.
108
+
109
+ ## Documentation
110
+
111
+ - [Documentation wiki](docs/README.md)
112
+ - [Usage guide](docs/usage.md): workflow, outputs, folder watching, session notes, speaker labels, headless machines
113
+ - [Obsidian](docs/obsidian.md): the setup wizard, what lands in the vault, sync
114
+ - [Configuration reference](docs/configuration.md)
115
+ - [Whisper model profiles and measured trade-offs](docs/model-profiles.md)
116
+ - [Architecture and data lifecycle](docs/architecture.md)
117
+ - [Troubleshooting](docs/troubleshooting.md)
118
+ - [Privacy and security](docs/privacy-and-security.md)
119
+ - [Development guide](docs/development.md) and [changelog](CHANGELOG.md)
120
+
121
+ ## What it does
122
+
123
+ 1. A user-level systemd path unit starts a cycle when a drive mounts under `/media/$USER`, `/run/media/$USER`, or `/mnt`; a timer also runs a cycle about once per minute.
124
+ 2. The cycle finds supported audio files inside the recorder directory (default `RECORD`) on mounted media, plus anything in `WATCH_DIRS`.
125
+ 3. It copies new recordings to a local archive and verifies the copy with SHA-256.
126
+ 4. It deduplicates future scans using SQLite, then transcribes queued recordings locally with faster-whisper, optionally labelling speakers.
127
+ 5. It writes `.json` segments, `.txt` text, a Markdown transcript note, and a `.complete.json` marker that confirms all outputs finished.
128
+ 6. It groups the finished recordings into sessions and writes one session note per sitting, with an AI summary when a key is configured.
129
+ 7. During transcription a desktop progress dialog shows the active file, number of files, percentage, and ETA; a notification announces the finished notes.
130
+
131
+ ## Privacy and safety
132
+
133
+ - Transcription and speaker labelling are local. Audio is not uploaded by this project.
134
+ - AI summaries are off until you pick a backend. Then transcript text, never audio, goes to that backend: your own machine with Ollama, the provider behind a subscription tool such as Codex or Claude Code, or OpenRouter.
135
+ - Source audio on the USB drive is never deleted by default (`PURGE_DEVICE=0`), and files in `WATCH_DIRS` are never deleted at all.
136
+ - Do not commit `config.env`: it can contain API keys. The included `.gitignore` excludes it and all runtime data.
137
+
138
+ ## Requirements
139
+
140
+ - Linux with a user systemd session (desktop or headless)
141
+ - Python 3.10+
142
+ - `ffmpeg` for audio decoding
143
+ - `git` for the one-line installer
144
+ - Optional: `zenity` for the progress window, `libnotify-bin` for notifications, `python3-gi gir1.2-gtk-4.0 gir1.2-adw-1` for the control panel window (already present on most desktops; otherwise the panel opens in a browser)
145
+ - Optional: `pipx` for the package install
146
+ - Internet access the first time faster-whisper downloads the configured model
147
+
148
+ On Ubuntu/Debian:
149
+
150
+ ```bash
151
+ sudo apt install python3-venv ffmpeg zenity libnotify-bin git
152
+ ```
153
+
154
+ ## Install
155
+
156
+ Three ways to the same installation. Whichever you pick, settings live in `~/.local/share/usb-audio-transcriber/config.env` (`config.example.env` documents every option), the timer and the plug-in trigger become user systemd units, and **USB Audio Transcriber** lands in your app menu.
157
+
158
+ **The one-line installer** from [Quick start](#quick-start): clones the source under `~/.local/share/usb-audio-transcriber/src`, creates a virtual environment, runs the setup wizard on a fresh configuration, and enables the units. Run it again to update.
159
+
160
+ **From a clone:**
161
+
162
+ ```bash
163
+ git clone https://github.com/aaacharlie/usb-audio-transcriber.git
164
+ cd usb-audio-transcriber
165
+ ./install.sh # add --with-diarization for speaker labels
166
+ ```
167
+
168
+ **With pipx**, which keeps the program in its own environment and gives you a `usb-audio-transcriber` command:
169
+
170
+ ```bash
171
+ sudo apt install pipx ffmpeg zenity libnotify-bin
172
+ pipx install git+https://github.com/aaacharlie/usb-audio-transcriber # or, once published: pipx install usb-audio-transcriber
173
+ usb-audio-transcriber install # add --with-diarization for speaker labels
174
+ ```
175
+
176
+ `usb-audio-transcriber install` does what `install.sh` does apart from the virtual environment (pipx made one): the data folder, `config.env`, the doctor gate, the wizard, the units, and the menu entry. Every script is then a subcommand: `usb-audio-transcriber doctor`, `sessions list`, `search roof leak`, `model-cache status both`, `panel open`, `setup`, `cycle`, `paths`.
177
+
178
+ ### Update an existing installation
179
+
180
+ One-line installer or clone:
181
+
182
+ ```bash
183
+ curl -fsSL https://raw.githubusercontent.com/aaacharlie/usb-audio-transcriber/main/bootstrap.sh | bash
184
+ # or, in the clone: git pull --ff-only && ./install.sh
185
+ ```
186
+
187
+ pipx:
188
+
189
+ ```bash
190
+ usb-audio-transcriber update # pipx upgrade, then install again
191
+ ```
192
+
193
+ Either way the installed `config.env` is preserved.
194
+
195
+ ### Uninstall
196
+
197
+ ```bash
198
+ ./uninstall.sh # one-line installer or clone
199
+ usb-audio-transcriber uninstall && pipx uninstall usb-audio-transcriber # pipx
200
+ ```
201
+
202
+ This removes deployed code, the virtual environment, and the user systemd units. It deliberately preserves `config.env`, runtime state, archives, transcripts, and model caches so uninstalling cannot silently erase user data.
203
+
204
+ ## Status and logs
205
+
206
+ ```bash
207
+ systemctl --user status usb-audio-transcriber.timer
208
+ systemctl --user status usb-audio-transcriber-plug.path
209
+ systemctl --user status usb-audio-transcriber.service
210
+ journalctl --user-unit=usb-audio-transcriber.service -f
211
+ ```
212
+
213
+ The pipeline also logs to `~/.local/share/usb-audio-transcriber/var/logs/pipeline.log`.
214
+
215
+ Run the built-in diagnostic after installation or whenever setup fails:
216
+
217
+ ```bash
218
+ ~/.local/share/usb-audio-transcriber/venv/bin/python \
219
+ ~/.local/share/usb-audio-transcriber/bin/doctor.py
220
+ ```
221
+
222
+ It checks configuration, required commands and Python packages, writable output locations, the timer, and the plug-in trigger without creating recordings or transcript data.
223
+
224
+ ## Configuration
225
+
226
+ `config.example.env` documents all options. The ones people change:
227
+
228
+ - `VAULT_DIR`: where Markdown notes go; the wizard points it at a folder inside your Obsidian vault.
229
+ - `WATCH_DIRS`: extra folders to scan, colon-separated, for synced phone memos or shares.
230
+ - `RECORDER_DIR` and `AUDIO_EXTS`: the folder name and file types your recorder uses.
231
+ - `WHISPER_MODEL_PROFILE`: `fast`, `accurate`, or `both`.
232
+ - `SUMMARY_BACKEND` and its companions: optional AI summaries through a subscription command-line tool, a local Ollama model, or OpenRouter; `SESSION_SUBJECT` tells the model the topic.
233
+ - `SESSION_GAP_MIN`: how long a silence has to be before a new session starts (20 minutes).
234
+ - `DIARIZATION` and `HF_TOKEN`: optional speaker labels.
235
+ - `HEADLESS` and `NOTIFY`: desktop window and notifications, `auto` by default.
236
+ - `PANEL_BIND` and `PANEL_PORT`: where the control panel listens; `0.0.0.0` lets your phone open it.
237
+ - `PURGE_DEVICE`: leave at `0` unless you explicitly want copied recordings removed from the USB device.
238
+
239
+ ### Whisper model choices
240
+
241
+ | Profile | Model | Pros | Cons |
242
+ | --- | --- | --- | --- |
243
+ | `fast` | `distil-large-v3` | Fastest supported option; lower disk, RAM, and CPU cost | Can be less reliable on distant, overlapping, or otherwise difficult speech |
244
+ | `accurate` | `large-v3` | Best accuracy-oriented option; more robust on difficult audio | Substantially slower on CPU; about 2.9 GiB of disk cache |
245
+ | `both` | both models | Produces a direct A/B comparison from the same recording | Takes the combined runtime and disk space of both models |
246
+
247
+ In one real CPU benchmark of a 57m 45s recording, `distil-large-v3` finished in 16m 56s while `large-v3` took 89m 57s: 5.31 times longer. That run was CPU-only on a GEEKOM X16 laptop (NX16AM): an Intel Core Ultra 9 185H (16 cores / 22 threads), 32 GB RAM, integrated graphics, running Ubuntu 26.04 LTS with GNOME — a consumer laptop, not a GPU workstation. Treat it as one hardware/audio data point, not a universal benchmark. See [Whisper model profiles](docs/model-profiles.md) for the complete result and interpretation.
248
+
249
+ Models are loaded into RAM only while they are being used, and downloaded weights stay in the Hugging Face disk cache. Manage them with `bin/model-cache.py status|download|remove`, and compare models on one file without touching the live queue with `bin/benchmark-models.py`; both are described in the [usage guide](docs/usage.md).
250
+
251
+ ## Development checks
252
+
253
+ ```bash
254
+ python3 -m py_compile bin/*.py
255
+ bash -n install.sh uninstall.sh bootstrap.sh bin/run-cycle.sh
256
+ python3 -m unittest discover -s tests -v
257
+ ```
258
+
259
+ See [CONTRIBUTING.md](CONTRIBUTING.md) before submitting a change. Report security issues privately according to [SECURITY.md](SECURITY.md).
260
+
261
+ ## License
262
+
263
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,26 @@
1
+ # Security policy
2
+
3
+ ## Supported version
4
+
5
+ Security fixes are applied to the current `main` branch and included in the next tagged release. Only the latest release and `main` are supported; there are no separate support windows for older versions.
6
+
7
+ ## Reporting a vulnerability
8
+
9
+ Use GitHub's private vulnerability reporting feature for this repository when available. If it is unavailable, contact the repository owner privately through their GitHub profile rather than opening a public issue.
10
+
11
+ Include:
12
+
13
+ - affected component and commit
14
+ - reproduction steps using non-sensitive test data
15
+ - realistic impact
16
+ - suggested mitigation, if known
17
+
18
+ Do not attach API keys, private recordings, transcripts, local configuration, filesystem listings containing sensitive names, or other personal data.
19
+
20
+ ## Expected response
21
+
22
+ This is a small open-source project without a guaranteed response SLA. Reports will be acknowledged and evaluated as availability permits. Confirmed issues should be fixed privately before coordinated disclosure when practical.
23
+
24
+ ## Scope reminders
25
+
26
+ A report is especially relevant when it concerns unintended audio/transcript disclosure, unsafe source deletion, command execution, credential exposure, path traversal, or bypass of deduplication/archive integrity guarantees. General upstream issues in faster-whisper, CTranslate2, requests, Hugging Face, OpenRouter, Python, systemd, or Zenity should also be reported to the affected upstream project.