Plug in your dictaphone. Run one command. Every recording is transcribed, searchable, and taggable.
Pasithea is built around a simple daily ritual: carry a small USB dictaphone, record your thoughts throughout the day, and dock it when you're done. Plug it into your Mac, run the app, and everything new on the device is pulled in, transcribed via MacWhisper's command-line tool, and waiting for you on a local webpage β searchable, taggable, editable. No cloud, no accounts, no database. The entire catalog lives in one JSON file.
Manual drops still work too β anything you drag into recordings/ (an AirDropped voice memo, a Zoom recording) gets picked up the same way. But the primary workflow is the dock: dictaphone in, run the app, transcripts out.
- Dictaphone auto-import β on every launch (and on demand via the sidebar button), Pasithea looks for your dictaphone's USB volume and pulls any new recordings out of its
RECORDfolder intorecordings/. Nothing is deleted from the device β recordings are copied, not moved. - Automatic transcription β anything that lands in
recordings/, whether from the dictaphone or dropped in by hand, is transcribed via MacWhisper'smwCLI and appears on the page within seconds. No manual step. - Rename-safe, re-plug-safe β every file is identified by content, not filename or path. Renaming a file, or re-plugging a dictaphone that still has old recordings on it, is recognized as already known β never re-imported, never re-transcribed, never duplicated.
- Full-text search β search across filenames and transcript text, with matches highlighted inline.
- Tagging β organize recordings with tags (defaults:
idea,rambling,todo). Add or remove tags from the sidebar at any time; each tag gets a consistent accent color. Click a tag in the sidebar to filter the list down to just that tag. - Editable transcripts, titles & timestamps β MacWhisper isn't always perfect, so every transcript is directly editable in place. Every entry also gets an editable title (separate from the underlying filename) and an editable "recorded at" date/time, in case the file's timestamp doesn't match when you actually recorded it.
- Delete, safely β deleting an entry moves the underlying file to
recordings/.trash/instead of destroying it, and removes it from the catalog. - Four Catppuccin themes β Latte, FrappΓ©, Macchiato, and Mocha, switchable from a dropdown, remembered per-browser.
- No database β the whole catalog is a single
data.jsonfile. Easy to back up, inspect, or edit by hand.
- Record throughout the day on your dictaphone.
- At the end of the day, plug it into your Mac via USB.
- Run
python3 app.py(or, if it's already running, click Import from dictaphone in the sidebar). - Open http://127.0.0.1:5151 β your new recordings are already transcribed, or finishing up.
- Search, tag, rename, and edit as needed. The originals stay safely on the dictaphone.
Pasithea is a small Flask app (app.py) with background worker threads, plus a static HTML/CSS/JS frontend β no build step, no JavaScript framework.
- On startup (and whenever you click Import from dictaphone), Pasithea checks
/Volumes/<dictaphone_volume_name>/<dictaphone_folder>(by defaultPASITHEA/RECORD). Any audio file found there whose content isn't already in the catalog gets copied intorecordings/. The device itself is never modified. - A background thread (
watchdog) also watchesrecordings/directly, for anything dropped in by hand. - When a file shows up (from either path) and its size has stopped changing (so we don't grab a half-written file), it's checked against the catalog:
- If a file at that exact path is already known, nothing happens.
- If not, its content is hashed (SHA-1). A match against an existing entry means this is the same recording under a new name β renamed, re-imported, whatever β the catalog entry is updated in place, no re-transcription.
- No match means it's genuinely new β a catalog entry is created and the file is queued for transcription.
- A single worker thread pulls jobs off the queue and runs
mw transcribe <file> --format txt, MacWhisper's command-line tool, capturing the transcript from stdout. - The result (or error, if
mwisn't installed or transcription fails) is written back intodata.json. - The frontend polls the Flask API every few seconds and re-renders the list β no page refresh needed to see a new recording finish transcribing. (Polling pauses automatically while you're mid-edit on a title or transcript, so it never overwrites what you're typing.)
Everything β transcripts, titles, tags, timestamps, status β lives in data.json. There's no database server and no schema migrations; it's just a JSON file read and rewritten (atomically) on every change.
pasithea/
βββ app.py Flask server, dictaphone import, folder watcher, transcription worker, API
βββ config.json Settings: dictaphone volume/folder, recordings path, tag list, host/port
βββ data.json The entire catalog (auto-created, do not edit while the server runs)
βββ requirements.txt
βββ icon.svg Source app icon
βββ recordings/ Watched folder β dictaphone imports and manual drops both land here
β βββ .trash/ Deleted recordings land here instead of being destroyed
βββ static/ Frontend (served directly by Flask, no build step)
βββ index.html
βββ style.css Catppuccin theme definitions + all styling
βββ app.js Search, tagging, theming, polling, all UI logic
βββ favicon.svg
βββ pasithea-banner.png
recordings/is the single working folder for the whole app β it's where dictaphone imports get copied to, and also where you can drop files by hand. Created automatically if it doesn't exist. Anything with an extension listed inconfig.json'saudio_extensionsgets picked up (.mp3,.wav,.m4a,.mp4,.mov,.flac,.aac,.ogg,.mkv,.webm,.cafby default).recordings/.trash/is where deleted recordings go. It's excluded from the watcher, from rescans, and from dictaphone re-import checks, so nothing in there gets picked back up. Files aren't permanently deleted by the app β clear this folder yourself if you want to reclaim disk space.data.jsonis the entire database: an array of entries, each with an id, filename, title, tags, transcript, status, content hash, and timestamps. Safe to read, back up, or hand-edit while the server is stopped.config.jsonholds the settings described below.
Nothing needs to exist on the dictaphone side beyond its own RECORD folder β Pasithea only ever reads from it and copies out, it never writes back to or deletes anything on the device.
- Python 3 with Flask and watchdog (see Setup).
- MacWhisper installed, with its command-line tool enabled: open MacWhisper β Settings β Advanced β Command-Line Tool β Install. This installs the
mwbinary that Pasithea shells out to. Without it, new recordings will show a clear "MacWhisper CLI not found" error on the page instead of transcribing. - A USB dictaphone (optional, but this is the intended workflow). Any cheap USB flip-plug voice recorder works β Pasithea was built against a Liebssen digital voice recorder, which mounts as a plain USB drive with a
RECORDfolder containing timestamped.WAVfiles. Rename its USB volume (in Finder, like any drive) to matchdictaphone_volume_nameinconfig.jsonβPASITHEAby default.
pip3 install -r requirements.txt
python3 app.pyThen open http://127.0.0.1:5151. If your dictaphone is plugged in, its recordings are already imported and transcribing. Otherwise, drop a recording into recordings/ and watch it appear.
Edit config.json (the server reads it fresh on relevant actions, so most changes just need a restart):
| Key | Description |
|---|---|
dictaphone_volume_name |
The name of your dictaphone's USB volume, as it appears under /Volumes/. Matched case-insensitively. Set to null to disable dictaphone auto-import entirely. |
dictaphone_folder |
The subfolder on the dictaphone containing recordings. Defaults to RECORD, matched case-insensitively. |
recordings_path |
Folder to watch. Relative paths resolve from the project root; you can also point it at an absolute path elsewhere on disk. |
tags |
The default/available tag list. Also editable live from the sidebar's "add tag" field. |
audio_extensions |
File extensions the watcher (and dictaphone import) picks up. |
host / port |
Where the local web server binds. |
- This is meant to run on your own Mac for personal use β the dev server (
app.run(...)) is not intended to be exposed to the network. - Transcription is sequential (one file at a time) via a single worker thread, so dropping a batch of recordings at once β or importing a fresh dictaphone β will transcribe them one after another rather than all in parallel.
- Dictaphone import only ever reads from
RECORD/and copies intorecordings/. It never deletes or writes anything back to the device.
