orvanitag/README.md

5.9 KiB

contexts projects tags
frrreee
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 <command> [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:

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.