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
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user