--- contexts: - frrreee projects: [] tags: - fool - writeme --- # Orvani Tagger (From עוֹרְבָנִי, the Hebrew name for th Eurasian Jay) Platform independent, folder-based, and portable tagging system for organizing and searching files. The command line interface and organizing principles are heavily inspired by the todo.txt standard. Every file can be assigned projects, contexts, and tags and have a descriptive note. Exactly like tasks in todo.txt, files can be categorized by freely defined contexts, defined by the `@` prefix or projects, defined by the `+` prefix. In addition, the `#` prefix defines hashtags that can be assigned. So a as an example an audio-file could be assigned to project `+album` and have a context of `@drums` and tagged with `#boombap`, `#lofi`, `#snare`. It's completely up to you how you use the different means of categorization. ## Installation ``` $ uv tool install 'orvanitag[all]' ``` Requires Python 3.11 or newer. The core only needs rich. Reading and writing embedded metadata uses optional extras: `audio` (pytaglib, needs the TagLib C library), `pdf` (pypdf), `office` (olefile, only for legacy `.doc`/`.xls`/`.ppt`) and `markdown` (python-frontmatter). Plugins whose extra is not installed are skipped. ## Command Line Tool ``` $ otag [arguments ...] [flags] ``` Every command has a short alias, listed in brackets. `otag -h` prints an overview. Words starting with `+`, `@` or `#` become projects, contexts and tags; all other words form the note. Quote hashtags in the shell (`'#lofi'`), because an unquoted `#` starts a comment. ### Tagging `add` (`a`) adds projects, contexts, tags and a note to an existing file or folder. Adding to an already tagged file merges the tags and replaces the note. ``` $ otag add song.mp3 +album @drums '#lofi' '#snare' first take ``` `remove` (`r`) removes the given projects, contexts and tags. Without any, it removes the whole entry. ``` $ otag remove song.mp3 '#snare' $ otag remove song.mp3 ``` ### Searching `list` (`ls`, `l`) searches the current directory tree and prints the matching files with their tags. Without a query it lists every tagged file. ``` $ otag ls '#lofi' @drums ``` A term with a prefix only matches that category; a term without a prefix matches any category and the note. Terms match partially (`lof` finds `#lofi`) and all terms have to match. | Flag | Effect | |---|---| | `-e`, `--exact` | Match whole terms only | | `-or`, `--either` | Match any term instead of all | | `--files` | Only show files | | `-d`, `--dirs` | Only show folders | | `-p`, `--path PATH` | Search another directory tree | | `-r`, `--recursive` | Skip subdirectories (despite its name; subdirectories are included by default) | | `-c`, `--cache` | Read the index from `.otag.cache.json`, which is rebuilt whenever an `.otag` file changed | | `-dc`, `--delete-cache` | Delete `.otag.cache.json` before searching | ### Copying, Moving and Deleting `copy` (`cp`) and `move` (`mv`) transfer the tags of a file to another path. By default only the tags are transferred; `-f` (`--file-operation`) also copies or moves the file itself. The destination needs a directory part, so write `./new.mp3` rather than `new.mp3`. With `-f`, a destination ending in `/` keeps the file name. ``` $ otag cp song.mp3 ./song-copy.mp3 $ otag mv -f song.mp3 archive/ ``` `del` (`rm`) removes the entry of a file; `-f` also deletes the file. ``` $ otag rm -f song.mp3 ``` ### Maintenance `clean` (`c`) removes entries whose files no longer exist, in the given folder (default: current) and its subdirectories. `purge` (`p`) deletes all `.otag` and `.otag.cache.json` files in the given folder and its subdirectories. This removes every tag in that tree and cannot be undone. ``` $ otag clean $ otag purge old-project/ ``` ### Embedded Metadata `scan` (`s`) reads tags that are embedded in files and adds them to their entries, in the given folder (default: current) and its subdirectories. `write` (`w`) writes the entry of a file into the file itself, using the named plugin. ``` $ otag scan music/ $ otag write id3 song.mp3 ``` | Plugin | File types | Where the tags are stored | |---|---|---| | `id3` | `.mp3` `.wav` `.flac` `.ogg` | The comment tag | | `pdf` | `.pdf` | The keywords field | | `office` | `.docx` `.xlsx` `.pptx` `.odt` `.ods` `.odp`, reading also `.doc` `.xls` `.ppt` | The description field | | `frontmatter` | `.md` `.txt` `.markdown` `.rst` | `projects`, `contexts`, `tags` and `note` keys of the YAML front matter | | `hash//` | Source code such as `.py` `.js` `.c` `.rs`, plus `.md` `.txt` | A `#// +project @context #tag note` line within the first five lines | ### Import and Export `export` (`e`) writes all entries of the file's directory tree to a text file (default: `context.txt`), one `path --- tags` line per file. `import` (`i`) reads such a file back and adds the tags that are missing. ``` $ otag export backup.txt $ otag import backup.txt ``` ## How Tags Are Stored Each folder keeps the entries of its files and subfolders in a plain text `.otag` file, so tags travel with the folder when it is copied, synced or archived. Every line holds one entry in the same syntax as the command line: the name, ` --- `, then projects, contexts, tags and the note. Folder names end with `/`. ``` Boombap Loop.wav --- +album @drums #boombap #lofi song.mp3 --- +album @drums #lofi #snare first take stems/ --- @archive ``` The file can be edited by hand. A line without ` --- ` stops otag with an error naming the file and line, so nothing gets overwritten until it is fixed. Note words starting with `+`, `@` or `#` are read as tags. An optional `.otag.toml` in the same folder configures `scan` and `write` there: ```toml plugins = ["id3", "pdf"] [id3] types = [".mp3", ".flac"] [id3.map] tags = "genre" ``` `plugins` limits which plugins run (`hash//` is listed as `hashdoubleslash`), `types` overrides the file types a plugin handles, and `id3.map` additionally reads an audio tag (here `GENRE`) into a category.