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

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