On this page
Folder module (draft 0.2)
Status: draft for comment. Namespace: org.openmusiccollection.folder. Needs: Core 1.0.
A music collection should outlive every app that touches it. This module lays a collection out as a folder of plain-text files the person keeps wherever they like (a disk, Dropbox, iCloud, a git repository). The files are readable in any text editor, and the folder opens as an Obsidian vault as it is. It follows Steph Ango's file over app idea: apps come and go, files can last, so the files are what the person owns.
The folder and the collection file hold exactly the same collection: either can be converted into the other without losing anything, including the person's own notes and properties.
Standards this module reuses
It invents as little as possible. Where Obsidian already has an open or documented format, the folder uses it unchanged:
| Standard | Used for |
|---|---|
| CommonMark Markdown | Every note |
YAML 1.2 frontmatter as Obsidian Properties: flat names; text, list, number and checkbox values; links written "[[Note]]" | The fields of items, lists, sources and the collection |
Obsidian wikilinks ([[Note]]) | References: a list's entries, an item's sources |
JSON Canvas 1.0 (MIT), .canvas files | An optional visual board for a list, such as a DJ set with its transitions |
Obsidian Bases (.base, YAML) | An optional ready-made table of the collection. Not normative |
Obsidian Properties are flat: no nested values. So each note keeps the fields that fit as properties, and puts the rest (copies, provenance, extensions) in one fenced JSON block, still in plain text.
Layout
My Music/
├── Collection.md the header
├── Collection.base optional: a table of the collection in Obsidian
├── Items/Mira Holt - Night Drive.md one note per recording
├── Releases/Mira Holt - Night Drive EP.md one note per release (album, single, EP)
├── Lists/Late sets.md one note per list
├── Lists/Late sets.canvas optional: a board for the list
├── Sources/Spotify.md one note per source
├── Records/Mira Holt.md module records, such as follows
└── Templates/Record.md optional: a template for typing a record by hand
This layout is what producers write. Readers don't depend on it: everything that matters is in the notes' contents (see Reading a folder). The person, or another app, can rename notes, move them into any folders (a folder per artist, per year) or leave them all in one pile, and the collection reads back the same.
File names are for people:
| Note | Name |
|---|---|
| Item | <artists> - <title>; more than 40 characters of artists become <first artist> +<n> |
| List, source | <name> |
| Module record | <name> |
- Characters that file systems or wikilinks don't allow (
/ \ : * ? " < > | # ^ [ ]) become-. - Names longer than 120 characters are cut in the middle (
…), keeping the end, which often tells songs apart ("… II. Aristophanes. Allegretto"). Trailing spaces and dots are removed: they break links, and Windows refuses them. - When a song and a release share a name (a single and its song), the release adds
(release)and the song keeps the plain name. - Names that clash are told apart by the song, never by a counter: an item adds its album, then its
year, then its length (
Ojerime - Jetset (Bad Influence),… (4m01s)); a list adds its source's name; a source its account. Only when nothing else differs does a name get a short hash of the id. A name depends only on its record and on the records that share it, never on their order. - A producer MUST NOT give two notes the same file.
Notes
Items
---
omc: item
id: i1
kind: recording
title: Night Drive
artists:
- Mira Holt
album: Night Drive EP
duration_ms: 312000
bpm: 124
key: Am
isrc: QZES72600001
sources:
- "[[Spotify]]"
- "[[Bandcamp]]"
tags:
- late night
---
Heard it at the Basement in March. The breakdown at 2:40 is the moment.
```omc
{
"copies": [
{ "source": "s1", "ref": "0exampleTrackId000001", "url": "https://open.spotify.com/track/0exampleTrackId000001" },
{ "source": "s2", "ref": "2854920395", "ownership": "digital" }
]
}
```
omc and id. omc says what kind of OMC record the note is (item, list, source,
collection, or a module record's type); id is the record's id. The id is this collection's
id for the record: links and list entries depend on it, so don't change it by hand, and don't
copy it into a new note (give a new note a fresh id, or none, see
Notes made by hand). It is not a global identifier: the same song in
someone else's collection has another id.
Properties. Item fields that fit as properties are written as properties, with the core's names and values:
kind,title,artists(list),album,album_artists(list),composers(list),release_type,duration_ms,release_date(text: OMC dates may be partial),label,catalog_number,country,genres(list),bpm,key,artwork_url,rating,added_at(text, as in the core),play_count,last_played_at;- the identifiers
isrc,upc,musicbrainzandiswc, taken out ofids.
The note's text is the item's note, without the omc block.
sources lists links to the notes of the sources the item has copies on, and releases to
the notes of the releases its copies came from (so a release's note lists its songs as backlinks).
Both are written for people (they make Obsidian's backlinks and graph work) and derived:
producers write them, importers MUST ignore them; the data is in the copies.
The omc block. A fenced code block with the info string omc, holding a JSON object with the
record's remaining members: copies, provenance, via, other ids, ext, and members the
producer doesn't know. There is at most one per note, and none when nothing remains.
The person's own data. Any other property (tags, aliases, anything the person adds) is the
person's. Importers keep these in the record's ext["org.openmusiccollection.folder"].properties,
as written, and producers write them back as properties. So a collection file carries them to any
app, and back into a folder unchanged. A person typing a property never has to pick a namespace.
Lists
---
omc: list
id: l1
ordered: true
role: curated
position: 2
source: "[[Spotify]]"
parent: "[[Gigs]]"
ref: 0examplePlaylistId0001
---
For the long drive home. Opened with these at the March gig.
## Entries
1. [[Mira Holt - Night Drive]] <!-- omc {"item":"i1","ref":"0exampleTrackId000001","added_at":"2026-03-02T21:10:00Z","note":"Open with this"} -->
2. [[Lumen Arcade - Glass Tides]] <!-- omc {"item":"i2"} -->
```omc
{ "source": "s1", "parent": "l7" }
```
- Properties:
ordered(checkbox),folder(checkbox),position,color,role,ref,created_at;- The list's name is its
nameproperty; producers also use it as the file name. source(a link to the source note),parent(a link to the folder's note) andboard(a link to the list's board) are derived, for people: the source's and the folder's ids are in theomcblock, and importers ignore the links as data. A list typed by hand, with no ids in a block, has itssourceandparentlinks followed.- A
filteris in theomcblock.
- Entries are the list items under a heading
Entries(any level): numbered for an ordered list, bulleted for an unordered one, in order.- Each has a link to the item's note, for people, and its data on the same line in an HTML
comment,
<!-- omc {…} -->, starting with the item's id ("item"), thenref,added_at,note,ext. Moving a line moves its data. - The id decides which item an entry is: see Reading a folder.
- Each has a link to the item's note, for people, and its data on the same line in an HTML
comment,
- The rest of the body is the list's
description. A folder (folder: true) is a list note with no entries; its lists link to it inparent.
Sources
Properties omc: source, id, platform, name, account, account_name, descriptor; an
omc block for ext and unknown members. The core has no note on sources, so the body is the
person's own, kept in ext["org.openmusiccollection.folder"].notes.
Collection
Collection.md holds the header:
- Properties:
omc: collection,version,generated_at, andnamewhen the collection has one. Producers use the name as the folder's name when they create it (a vault called "Anna's records"); - The
omcblock:generator,counts,modulesandext.
counts describes the files at the time they were written. Importers recompute it.
Module records
Properties omc: <type> (for example org.openmusiccollection.feeds/follow) and id, plus
the record's flat text, number, checkbox and list-of-text members as properties; the rest in the
omc block.
Names written before
Folders written by draft 0.1 used omc_type and omc_id. Readers MUST still accept them (a
note with either pair is read the same), and producers write omc and id.
Before the core had note, an item's body was kept in ext["org.openmusiccollection.folder"].notes,
and a list's in the same place, with description as a property. Readers read an item's body as
note, and a list's description property, when there is one, as description, with the body
kept as before. Producers write a collection file's older notes back as the body.
Notes made by hand
A folder is meant to be edited by people, so a note typed by hand is read like any other:
- Without an
id. A note withomcand noidgets one when it is read (derived from its path, so it stays the same until the note moves).omc-folder add-idswrites fresh ids into such notes once, so links to them keep working after a rename. - A template. A producer MAY write
Templates/Record.md, a release note (omc: item,kind: release) with its other properties left empty, for Obsidian's Templates feature, so a note made from it is an ordinary record. Readers skip the folderTemplatesat the folder's root. - A shelf. A release note with a
shelfproperty naming a source note (shelf: "[[My records]]") gets a copy on that source, withownership: physicaland the note's id asref, unless theomcblock already has copies. So a person can catalogue a record with properties alone:
---
omc: item
kind: release
title: Night Drive EP
artists:
- Mira Holt
release_date: "2026"
shelf: "[[My records]]"
---
Bought at the Basement gig, signed.
The shelf property is kept as written, and a producer writes it back.
Boards (JSON Canvas)
A list MAY have a board: a JSON Canvas file, linked from the list's board property
("[[Late sets.canvas]]"), written next to the note with the same name. Readers follow the link and
fall back to a .canvas with the note's name beside it.
- File nodes point at item notes; text nodes and edges carry whatever the person adds, such as transitions, cue notes or a mix plan.
- The board is the person's view of the list. The entries in the note stay authoritative.
- Importers keep the board whole in the list's
ext["org.openmusiccollection.folder"].canvas, and producers write it back unchanged. - A producer MAY draw a board for an ordered list that has none: one file node per entry, left to right, with an edge from each to the next.
Bases
A producer MAY write Collection.base, a table of items for Obsidian, and MUST NOT overwrite
an existing one. It is a convenience for Obsidian users, not part of the collection.
Reading a folder: contents first
A reader MUST get the same collection from a folder whatever its file names, folders and links look like: notes renamed by hand, moved by a sync tool or another app, or piled into one folder.
What a note is comes from
omcandid, never from its name or folder. Notes in any folder are read; notes and files withoutomcare the person's and are left alone; hidden folders (.obsidian,.git) are skipped. Both.mdand.markdownare read.What a record points at comes from ids in the contents: an entry's
"item", a list'ssourcein its block, an item's copies. When an entry's link and its id disagree, the id wins and the reader reports it; to point an entry at another item, replace the whole line.Links are followed only where there is no id (a line typed by hand, a list made by hand). Readers accept every link style Obsidian writes and standard Markdown links:
- a bare name,
[[Night Drive]], which must match exactly one note; - a path from the folder's root,
[[Items/Night Drive]]; - a path relative to the linking note,
[[../Items/Night Drive]]; [Night Drive](../Items/Night%20Drive.md).
Aliases (
|…) and headings (#…) are ignored, and names match without regard to case or the.md.- a bare name,
Copies of a note (two notes with the same
omcandid, as sync tools leave behind as "conflicted copies") are read once when their contents are the same.Loosely written values are read for what they mean:
"124"as a number where the core wants a number,"true"as a checkbox, a single text value as a one-value list. Readers say when they do.
Converting
| From | To | Rule |
|---|---|---|
| A collection file | A folder | Write every record as above. Null members are left out (absent and null mean the same). |
| A folder | A collection file | Read every Markdown note with omc; rebuild each record from properties, the omc block, entries and the person's data; resolve links to ids. When any record carries the person's data, list this module in the header's modules. |
Converting a file to a folder and back MUST give the same records, null members aside. The reference tool checks this on every example.
Conformance
- A folder is valid when converting it gives a valid collection file.
- Two notes with the same
omcandidand different contents are an error. - An entry without an id whose link matches several notes, or none, is an error (several) or a warning (none, read as the link's name).
Open questions
Should an item's
tagsmap togenres, or stay the person's own?Two apps editing the same folder at once (the person's editor and a music app): last write per file wins. Is that enough?
Writing into an existing folder. A producer that updates a folder should keep every note where it is and under the name it has (finding it by
id), and name only new notes, so names and the person's own links never change under them. What happens to the note of a record the collection no longer has: delete it, or move it to an archive folder?The layout puts recordings in
Items/and releases inReleases/, so there "items" means only songs.Tracks/orRecordings/instead?
What was decided, and when, is in the changelog.