Files
Alexander 1374084135 Reorganize docs into v1 (beetfs) and v2 (new architecture)
docs/v1/ - Original beetfs documentation:
  - analysis.md, components.md, data-flow.md, drawbacks.md
  - features.md, modernization.md, rust-migration.md
  - benchmark-plan.md, benchmark-results.md, e2e-test-plan.md
  - README.md

docs/v2/ - New MusicFS architecture:
  - requirements.md: Full requirements spec (FR-1 to FR-25, NFR-1 to NFR-14)
    - P0: Multi-origin, plugins, CAS, control API
    - P1: Search, album art, prefetch, metadata sources
    - P3: HA, 10M+ files scalability
  - architecture.md: Google BlueDoc style design document
    - PlantUML diagrams for all components
    - Design requirements with quantitative targets
    - Alternatives considered, implementation plan
2026-05-12 16:46:37 +02:00

9.1 KiB

beetfs Feature Set

Overview

beetfs is a FUSE filesystem plugin for beets that presents your music library as a virtual filesystem organized by metadata. Files appear with paths derived from their database metadata, and reading file headers returns metadata from the beets database rather than the actual file tags.

Author: Martin Eve (2010)
License: GPLv3
Python: 2.7 (uses fuse-python)

Core Features

1. Virtual Metadata-Based Directory Structure

Files are presented in a configurable path format based on beets database fields:

$artist/$album ($year) [$format_upper]/$track - $artist - $title.$format

Example:

/mnt/beetfs/
├── Metallica/
│   └── 72 Seasons (2023) [FLAC]/
│       ├── 01 - Metallica - 72 Seasons.flac
│       ├── 02 - Metallica - Shadows Follow.flac
│       └── ...
├── Pink Floyd/
│   └── The Dark Side of the Moon (1973) [FLAC]/
│       └── ...

Available template variables:

  • $artist, $album, $title, $genre, $composer, $grouping
  • $year, $month, $day
  • $track, $tracktotal, $disc, $disctotal
  • $format, $format_upper (file extension)
  • $lyrics, $comments, $bpm, $comp

2. Metadata Overlay (Read)

When you read a file through beetfs, the metadata header is synthesized from the beets database, not read from the actual file on disk.

How it works:

  1. Open file → beetfs reads the real file from disk
  2. Parse the audio format header (FLAC/MP3)
  3. Replace metadata fields with values from beets database
  4. Return synthesized header + original audio data

Supported fields for overlay:

  • title, artist, album, genre (FLAC only currently)

Use case: Your files may have inconsistent or wrong tags, but beetfs presents them with the corrected metadata from your beets library.

3. Metadata Passthrough (Write)

When you write to file headers through beetfs, the changes are saved to the beets database, not to the actual file.

How it works:

  1. Application writes new metadata to file header region
  2. beetfs intercepts the write
  3. Parses the new metadata values
  4. Updates the beets database (lib.store(), lib.save())
  5. Regenerates the synthesized header

Result: Tag editors (Picard, Kid3, etc.) can edit metadata through beetfs, and changes persist in the beets database without modifying the original files.

4. Format Support

Format Read Metadata Overlay Write to DB
FLAC Full
MP3 Disabled
Other

FLAC Implementation:

  • Uses InterpolatedFLAC class extending mutagen
  • Reconstructs Vorbis comment block with DB values
  • Preserves audio data and other metadata blocks

MP3 Implementation:

  • Passthrough only (no interpolation)
  • self.bound = 0 disables header replacement

5. File Caching

Open files are cached in FileHandler objects:

  • First open: Load entire file into memory, parse headers
  • Subsequent opens: Reuse cached FileHandler
  • Reference counting for multiple opens
  • Release when reference count reaches zero

Memory impact: Each open file consumes ~filesize RAM.

FUSE Operations

Implemented (Functional)

Operation Description
getattr File/directory stat (size, mode, timestamps)
access Permission checking
opendir Open directory for listing
readdir List directory contents
releasedir Close directory
open Open file for reading/writing
read Read file contents
write Write to file (header region only)
release Close file
fgetattr Stat with file handle
statfs Filesystem statistics

Not Implemented (Return EOPNOTSUPP)

Operation Reason
create Read-only structure
mknod Read-only structure
mkdir Read-only structure
unlink Read-only structure
rmdir Read-only structure
symlink Not needed
link Not needed
rename Would break DB consistency
chmod Metadata-only FS
chown Metadata-only FS
truncate Would corrupt audio
utime Metadata-only FS

Usage

Mount

beet mount /mnt/beetfs

Unmount

fusermount -u /mnt/beetfs

Example Session

# Mount the filesystem
beet mount /mnt/music

# Browse by artist
ls /mnt/music/
# Metallica/  Pink Floyd/  The Beatles/  ...

# List an album
ls "/mnt/music/Metallica/72 Seasons (2023) [FLAC]/"
# 01 - Metallica - 72 Seasons.flac
# 02 - Metallica - Shadows Follow.flac
# ...

# Play through any music player
mpv "/mnt/music/Metallica/72 Seasons (2023) [FLAC]/01 - Metallica - 72 Seasons.flac"

# Edit tags (changes go to beets DB)
kid3 "/mnt/music/Metallica/72 Seasons (2023) [FLAC]/"

# Unmount
fusermount -u /mnt/music

Architecture

┌─────────────────────────────────────────────────────────────┐
│                     User Applications                        │
│              (mpv, Rhythmbox, Kid3, etc.)                   │
└─────────────────────────┬───────────────────────────────────┘
                          │ POSIX calls (open, read, write)
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                      Linux Kernel                            │
│                       FUSE module                            │
└─────────────────────────┬───────────────────────────────────┘
                          │ /dev/fuse
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                        beetfs                                │
│  ┌─────────────┐  ┌──────────────┐  ┌───────────────────┐   │
│  │ FSNode Tree │  │ FileHandler  │  │ InterpolatedFLAC  │   │
│  │ (in-memory) │  │   (cache)    │  │  (header synth)   │   │
│  └─────────────┘  └──────────────┘  └───────────────────┘   │
└────────┬────────────────┬───────────────────┬───────────────┘
         │                │                   │
         ▼                ▼                   ▼
┌─────────────┐  ┌─────────────────┐  ┌─────────────────┐
│ Beets DB    │  │  Real Files    │  │   Mutagen       │
│ (SQLite)    │  │  (on disk)     │  │   (parsing)     │
└─────────────┘  └─────────────────┘  └─────────────────┘

Limitations

Current Bugs (Non-Functional)

  1. Nested Methods Bug: Lines 758-1144 are indented inside access(), making FUSE operations unreachable
  2. Directory Tree Bug: FSNode.adddir() crashes when building tree for non-empty library

Design Limitations

  1. Memory Usage: Entire file loaded into RAM on open
  2. Mount Time: O(N) - loads all library items at mount
  3. No Lazy Loading: Full directory tree built upfront
  4. Single Format: Only FLAC has full metadata overlay
  5. No Real File Modification: Writes only update DB, not actual files
  6. Python 2.7 GIL: Single-threaded performance

Not Supported

  • Creating/deleting files or directories
  • Moving/renaming files
  • Modifying audio content
  • Album art / embedded images
  • Multi-value tags
  • Non-ASCII in some edge cases

Configuration

Currently hardcoded. Potential configuration points:

Setting Current Value Description
PATH_FORMAT $artist/$album ($year)... Directory structure template
METADATA_RW_FIELDS 17 fields Fields available for read/write
Caching Always on FileHandler caching behavior
Threading Disabled multithreaded = 0

Dependencies

  • Python 2.7
  • fuse-python
  • beets 1.4.x
  • mutagen (FLAC/MP3 parsing)

See Also