6.3 KiB
| contexts | projects | tags | |||
|---|---|---|---|---|---|
|
|
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; its prebuilt wheels include TagLib, building from source needs the TagLib C library), pdf (pypdf), office (olefile, only for legacy .doc/.xls/.ppt) and markdown (PyYAML). Plugins whose extra is not installed are skipped.
Command Line Tool
$ otag <command> [flags] [arguments ...]
Every command has a short alias, listed in brackets. otag -h prints an overview, otag <command> -h the arguments and flags of one command; flags go after the command. 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 |
-n, --no-recursive |
Skip subdirectories (also for clean and scan) |
-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 (--with-file) also copies or moves the file itself. With -f, an existing folder as destination keeps the file name. Without -f, a folder as destination gets the tags itself.
$ 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 .markdown .txt |
projects, contexts, tags and note keys of the YAML front matter; other keys, comments and formatting are left untouched |
comment |
Source code such as .py .sh .toml .js .c .go .rs .php .html |
An otag: +project @context #tag note comment line within the first ten lines, written in the file's comment style below any shebang, encoding, <!DOCTYPE> or <?php line |
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 configures scan and write for its folder and all subfolders. Each file uses the nearest .otag.toml above it, which replaces any further up as a whole:
plugins = ["id3", "pdf"]
[id3]
types = [".mp3", ".flac"]
[id3.map]
tags = "genre"
plugins limits which plugins run, types overrides the file types a plugin handles (write ignores types the plugin does not support), and id3.map additionally reads an audio tag (here GENRE) into a category.