Rename the track-file extension to .cbd-track.toml
.track.toml was too generic for files that only crabidy understands; the cbd- prefix makes them unmistakable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
4dd5f01217
commit
955c7ea5b4
|
|
@ -81,9 +81,9 @@ Consequences, accepted deliberately:
|
|||
resolution. **Link chains are structurally impossible** — see D3's "no
|
||||
links into `/fs`" rule.
|
||||
|
||||
### D3 — On-disk schema: TOML, extension `.track.toml`, exactly one playable
|
||||
### D3 — On-disk schema: TOML, extension `.cbd-track.toml`, exactly one playable
|
||||
|
||||
TOML per project convention. A file named `<anything>.track.toml` inside
|
||||
TOML per project convention. A file named `<anything>.cbd-track.toml` inside
|
||||
the configured root is a track node; everything else (other files, hidden
|
||||
entries) is ignored. Schema:
|
||||
|
||||
|
|
@ -174,7 +174,7 @@ direction: right
|
|||
|
||||
disk: Local disk {
|
||||
shape: cylinder
|
||||
tree: "root dir: dirs, *.track.toml"
|
||||
tree: "root dir: dirs, *.cbd-track.toml"
|
||||
}
|
||||
|
||||
server: crabidy-server {
|
||||
|
|
@ -185,7 +185,7 @@ server: crabidy-server {
|
|||
}
|
||||
|
||||
fsdy: fsdy::Client {
|
||||
parse: "parse + validate .track.toml"
|
||||
parse: "parse + validate .cbd-track.toml"
|
||||
map: "path <-> root-relative file (encoded segments)"
|
||||
}
|
||||
|
||||
|
|
@ -218,7 +218,7 @@ pb -> orch: ResolveTracks("/fs/mix", chunk_tx)
|
|||
orch -> fs: resolve_tracks_into (spawned)
|
||||
fs -> fs: "list dir, parse 3 track files"
|
||||
fs -> pb: "chunk of 3 Tracks (paths below)" {style.bold: true}
|
||||
pb -> orch: "GetTrackUrls(/fs/mix/a.track.toml)"
|
||||
pb -> orch: "GetTrackUrls(/fs/mix/a.cbd-track.toml)"
|
||||
orch -> fs: get_urls_for_track
|
||||
fs -> pb: "[/home/u/Music/a.flac]"
|
||||
pb -> orch: "GetTrackUrls(/tidal/...) # link track, rewritten path"
|
||||
|
|
@ -241,4 +241,4 @@ path.)
|
|||
- **Metadata drift** on link tracks (file says X, target now titled Y):
|
||||
accepted; the file is the user's curated metadata.
|
||||
- Open (future): tag-reading for bare audio files; multiple roots; a
|
||||
`%`-style creator that writes a `.track.toml` from inside the TUI.
|
||||
`%`-style creator that writes a `.cbd-track.toml` from inside the TUI.
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ pub const PROVIDER_ROOT: &str = "/fs";
|
|||
|
||||
/// Files with this suffix are serialized track nodes; everything else in
|
||||
/// the tree is ignored.
|
||||
pub const TRACK_FILE_SUFFIX: &str = ".track.toml";
|
||||
pub const TRACK_FILE_SUFFIX: &str = ".cbd-track.toml";
|
||||
|
||||
/// Provider settings, persisted as `fsdy.toml` next to the other crabidy
|
||||
/// config files.
|
||||
|
|
@ -60,7 +60,7 @@ pub enum TrackFileError {
|
|||
LinkIntoFs(String),
|
||||
}
|
||||
|
||||
/// The on-disk schema of a `*.track.toml` file. See
|
||||
/// The on-disk schema of a `*.cbd-track.toml` file. See
|
||||
/// `architecture/fs-provider.md` (D3) for the format documentation.
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct TrackFile {
|
||||
|
|
@ -214,7 +214,7 @@ impl Client {
|
|||
}
|
||||
|
||||
/// Lists a directory as a library node: subdirectories become queueable
|
||||
/// child nodes, `*.track.toml` files become tracks, each list sorted
|
||||
/// child nodes, `*.cbd-track.toml` files become tracks, each list sorted
|
||||
/// case-insensitively by file name.
|
||||
///
|
||||
/// Symlinks, hidden entries (dot-prefixed), non-UTF-8 names, and track
|
||||
|
|
@ -552,7 +552,7 @@ mod tests {
|
|||
"relative link",
|
||||
),
|
||||
(
|
||||
"title = \"x\"\n[playable]\nlink = \"/fs/other.track.toml\"\n",
|
||||
"title = \"x\"\n[playable]\nlink = \"/fs/other.cbd-track.toml\"\n",
|
||||
"link into /fs",
|
||||
),
|
||||
("title = \"x\"\nnot toml at all [", "invalid toml"),
|
||||
|
|
@ -569,7 +569,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn to_track_rewrites_the_path_only_for_links() {
|
||||
let lib_path = "/fs/mix/song.track.toml";
|
||||
let lib_path = "/fs/mix/song.cbd-track.toml";
|
||||
let linked = TrackFile::parse(
|
||||
"title = \"t\"\nartist = \"a\"\n[playable]\nlink = \"/tidal/artists/1/2\"\n",
|
||||
)
|
||||
|
|
@ -590,9 +590,9 @@ mod tests {
|
|||
#[tokio::test]
|
||||
async fn track_paths_need_the_suffix_and_the_provider_prefix() {
|
||||
let (client, _dir) = client_with_root().await;
|
||||
assert!(client.is_track_path("/fs/mix/song.track.toml"));
|
||||
assert!(client.is_track_path("/fs/mix/song.cbd-track.toml"));
|
||||
assert!(!client.is_track_path("/fs/mix"));
|
||||
assert!(!client.is_track_path("/tidal/song.track.toml"));
|
||||
assert!(!client.is_track_path("/tidal/song.cbd-track.toml"));
|
||||
assert!(!client.is_track_path("/fs"));
|
||||
}
|
||||
|
||||
|
|
@ -622,10 +622,10 @@ mod tests {
|
|||
fs::create_dir(dir.path().join("b-dir")).expect("mkdir");
|
||||
fs::create_dir(dir.path().join("A-dir")).expect("mkdir");
|
||||
fs::create_dir(dir.path().join(".hidden-dir")).expect("mkdir");
|
||||
write_track(dir.path(), "b song.track.toml", &url_track("b"));
|
||||
write_track(dir.path(), "A song.track.toml", &url_track("a"));
|
||||
write_track(dir.path(), "broken.track.toml", "not [ valid");
|
||||
write_track(dir.path(), ".hidden.track.toml", &url_track("h"));
|
||||
write_track(dir.path(), "b song.cbd-track.toml", &url_track("b"));
|
||||
write_track(dir.path(), "A song.cbd-track.toml", &url_track("a"));
|
||||
write_track(dir.path(), "broken.cbd-track.toml", "not [ valid");
|
||||
write_track(dir.path(), ".hidden.cbd-track.toml", &url_track("h"));
|
||||
fs::write(dir.path().join("cover.jpg"), b"jpg").expect("write");
|
||||
#[cfg(unix)]
|
||||
std::os::unix::fs::symlink(dir.path().join("b-dir"), dir.path().join("z-link"))
|
||||
|
|
@ -641,7 +641,7 @@ mod tests {
|
|||
let tracks: Vec<&str> = node.tracks.iter().map(|t| t.title.as_str()).collect();
|
||||
assert_eq!(tracks, vec!["a", "b"]);
|
||||
// Track paths are encoded segments under the node's path.
|
||||
assert_eq!(node.tracks[0].path, "/fs/A%20song.track.toml");
|
||||
assert_eq!(node.tracks[0].path, "/fs/A%20song.cbd-track.toml");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
@ -658,7 +658,7 @@ mod tests {
|
|||
async fn nodes_link_back_to_their_parent() {
|
||||
let (client, dir) = client_with_root().await;
|
||||
fs::create_dir_all(dir.path().join("a/b")).expect("mkdir");
|
||||
write_track(&dir.path().join("a/b"), "t.track.toml", &url_track("t"));
|
||||
write_track(&dir.path().join("a/b"), "t.cbd-track.toml", &url_track("t"));
|
||||
let node = client.get_lib_node("/fs/a/b").await.expect("node");
|
||||
assert_eq!(node.parent.as_deref(), Some("/fs/a"));
|
||||
assert_eq!(node.title, "b");
|
||||
|
|
@ -675,28 +675,28 @@ mod tests {
|
|||
fs::create_dir(&sub).expect("mkdir");
|
||||
write_track(
|
||||
&sub,
|
||||
"rel.track.toml",
|
||||
"rel.cbd-track.toml",
|
||||
"title = \"r\"\n[playable]\nfile = \"a.flac\"\n",
|
||||
);
|
||||
let abs_target = dir.path().join("elsewhere.mp3");
|
||||
write_track(
|
||||
&sub,
|
||||
"abs.track.toml",
|
||||
"abs.cbd-track.toml",
|
||||
&format!(
|
||||
"title = \"a\"\n[playable]\nfile = {:?}\n",
|
||||
abs_target.to_str().expect("utf8")
|
||||
),
|
||||
);
|
||||
write_track(&sub, "web.track.toml", &url_track("w"));
|
||||
write_track(&sub, "web.cbd-track.toml", &url_track("w"));
|
||||
write_track(
|
||||
&sub,
|
||||
"linked.track.toml",
|
||||
"linked.cbd-track.toml",
|
||||
"title = \"l\"\n[playable]\nlink = \"/tidal/artists/1/2\"\n",
|
||||
);
|
||||
|
||||
// Relative files resolve against the track file's directory.
|
||||
let urls = client
|
||||
.get_urls_for_track("/fs/mix/rel.track.toml")
|
||||
.get_urls_for_track("/fs/mix/rel.cbd-track.toml")
|
||||
.await
|
||||
.expect("relative file");
|
||||
assert_eq!(
|
||||
|
|
@ -705,20 +705,20 @@ mod tests {
|
|||
);
|
||||
// Absolute files pass through.
|
||||
let urls = client
|
||||
.get_urls_for_track("/fs/mix/abs.track.toml")
|
||||
.get_urls_for_track("/fs/mix/abs.cbd-track.toml")
|
||||
.await
|
||||
.expect("absolute file");
|
||||
assert_eq!(urls, vec![abs_target.to_str().expect("utf8").to_string()]);
|
||||
// URLs pass through.
|
||||
let urls = client
|
||||
.get_urls_for_track("/fs/mix/web.track.toml")
|
||||
.get_urls_for_track("/fs/mix/web.cbd-track.toml")
|
||||
.await
|
||||
.expect("url");
|
||||
assert_eq!(urls, vec!["https://example.org/s.mp3".to_string()]);
|
||||
// Link playables never resolve here: their tracks route to the
|
||||
// target provider, so landing here means a malformed request.
|
||||
let err = client
|
||||
.get_urls_for_track("/fs/mix/linked.track.toml")
|
||||
.get_urls_for_track("/fs/mix/linked.cbd-track.toml")
|
||||
.await
|
||||
.expect_err("link");
|
||||
assert_eq!(err, ProviderError::MalformedPath);
|
||||
|
|
@ -729,11 +729,11 @@ mod tests {
|
|||
let (client, dir) = client_with_root().await;
|
||||
write_track(
|
||||
dir.path(),
|
||||
"linked.track.toml",
|
||||
"linked.cbd-track.toml",
|
||||
"title = \"t\"\nartist = \"a\"\n[playable]\nlink = \"/tidal/artists/1/2\"\n",
|
||||
);
|
||||
let track = client
|
||||
.get_metadata_for_track("/fs/linked.track.toml")
|
||||
.get_metadata_for_track("/fs/linked.cbd-track.toml")
|
||||
.await
|
||||
.expect("metadata");
|
||||
assert_eq!(track.path, "/tidal/artists/1/2");
|
||||
|
|
@ -750,9 +750,9 @@ mod tests {
|
|||
let al2 = dir.path().join("artist/album2");
|
||||
fs::create_dir_all(&al1).expect("mkdir");
|
||||
fs::create_dir_all(&al2).expect("mkdir");
|
||||
write_track(&al1, "01.track.toml", &url_track("one"));
|
||||
write_track(&al1, "02.track.toml", &url_track("two"));
|
||||
write_track(&al2, "01.track.toml", &url_track("three"));
|
||||
write_track(&al1, "01.cbd-track.toml", &url_track("one"));
|
||||
write_track(&al1, "02.cbd-track.toml", &url_track("two"));
|
||||
write_track(&al2, "01.cbd-track.toml", &url_track("three"));
|
||||
|
||||
let (chunk_tx, chunk_rx) = flume::bounded(8);
|
||||
client
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
Built per `plan/fs-provider.md`: a second media provider (crate `fsdy`,
|
||||
`/fs`) that walks one configured root directory and treats
|
||||
`*.track.toml` files as serialized track nodes — metadata plus exactly
|
||||
`*.cbd-track.toml` files as serialized track nodes — metadata plus exactly
|
||||
one playable reference: a local audio file (absolute or relative to the
|
||||
track file), an http(s) URL, or a crabidy-internal link. The wire types
|
||||
are unchanged (architecture D1): the only new datastructure is the
|
||||
|
|
@ -30,6 +30,9 @@ are recorded in `architecture/fs-provider.md` (options + rationale).
|
|||
|
||||
### Deviations from plan / architecture (fs-provider)
|
||||
|
||||
- **Extension renamed to `.cbd-track.toml`** (user request, follow-up
|
||||
commit): the original `.track.toml` was too generic; the `cbd-` prefix
|
||||
makes the files unmistakably crabidy's.
|
||||
- **`TrackFileError::UrlScheme` carries only the scheme**, not the URL:
|
||||
the parse error ends up in skip-warnings, and a private stream URL may
|
||||
embed a token (quality gate "no file contents in logs"). The
|
||||
|
|
|
|||
Loading…
Reference in New Issue