1374084135
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
250 lines
9.1 KiB
Markdown
250 lines
9.1 KiB
Markdown
# beetfs Feature Set
|
|
|
|
## Overview
|
|
|
|
beetfs is a FUSE filesystem plugin for [beets](https://beets.io/) 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
|
|
|
|
```bash
|
|
beet mount /mnt/beetfs
|
|
```
|
|
|
|
### Unmount
|
|
|
|
```bash
|
|
fusermount -u /mnt/beetfs
|
|
```
|
|
|
|
### Example Session
|
|
|
|
```bash
|
|
# 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
|
|
|
|
- [e2e-test-plan.md](e2e-test-plan.md) - Test strategy and bug documentation
|
|
- [benchmark-plan.md](benchmark-plan.md) - Performance measurement methodology
|
|
- [benchmark-results.md](benchmark-results.md) - Current benchmark status
|