Rewrite all folder-level CLAUDE.md files; update root
net10.0 throughout; corrected EntryKey field name; documented *.Services split. Two new files (DeepDrftWeb.Services, DeepDrftContent.Services). FileDatabase README inaccuracies fixed (ImageVault, project target).
This commit is contained in:
@@ -1,45 +1,87 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
This file provides guidance to Claude Code when working with code in this repository.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
DeepDrftHome is a .NET 9 solution consisting of multiple projects that implement a dual-database media management system:
|
||||
DeepDrftHome is a **net10.0** solution consisting of eight projects implementing a dual-database media management system for the DeepDrft electronic music collective.
|
||||
|
||||
### Core Projects
|
||||
- **DeepDrftWeb**: Blazor Server/WebAssembly hybrid web application using MudBlazor UI framework
|
||||
- **DeepDrftWeb.Client**: Blazor WebAssembly client components
|
||||
- **DeepDrftContent**: Web API project providing content management endpoints with API key authentication
|
||||
- **DeepDrftModels**: Shared data models and entities
|
||||
- **DeepDrftTests**: NUnit test project with comprehensive FileDatabase tests
|
||||
- **NetBlocks**: External dependency located at `C:\lib\NetBlocks\`
|
||||
|
||||
- **DeepDrftWeb**: ASP.NET Core host. Blazor Web App with Server + WASM render modes. Owns the SQL-backed `api/track/page` endpoint, MudBlazor theme prerender, and TypeScript→JS audio interop.
|
||||
- **DeepDrftWeb.Client**: Blazor WebAssembly assembly. All interactive UI (pages, player stack, dark-mode plumbing, HTTP clients for both backends).
|
||||
- **DeepDrftWeb.Services**: Class library. EF Core domain logic: `DeepDrftContext`, `TrackConfiguration`, `Migrations`, `TrackRepository`, `TrackService`. Sharable between web host and CLI.
|
||||
- **DeepDrftContent**: ASP.NET Core host. Binary content API (`GET api/track/{id}` unauthenticated, `PUT api/track/{id}` ApiKey-protected). Returns audio bytes with optional WAV-aware offset streaming.
|
||||
- **DeepDrftContent.Services**: Class library. The FileDatabase implementation in full (Models, Services, Utils, Abstractions, Constants), `WavOffsetService`, `AudioProcessor`, content-side `TrackService`. Consumed by host and CLI.
|
||||
- **DeepDrftModels**: Shared contracts. `TrackEntity`, `TrackDto`, `PagingParameters<T>`, `PagedResult<T>`. Every project references this.
|
||||
- **DeepDrftCli**: Console app. Two modes: classic CLI (`add` / `list` / `help`) and Terminal.Gui (`gui`). Direct access to both databases (local admin tool, not a network client).
|
||||
- **DeepDrftTests**: NUnit test suite. Comprehensive FileDatabase tests (vault creation, media storage, indexing, factory patterns, utilities). Integration-focused with temp-directory test isolation.
|
||||
|
||||
External: **NetBlocks** (absolute path `C:\lib\NetBlocks\`). Provides `Result`, `ResultContainer<T>`, `ApiResult<T>`, `ApiResultDto<T>`.
|
||||
|
||||
### Database Architecture
|
||||
|
||||
The application uses a **dual-database approach**:
|
||||
**Dual-database approach** — browser never reaches storage directly:
|
||||
|
||||
1. **SQL Database (SQLite)**: Stores metadata and track information via Entity Framework
|
||||
- Located: `../Database/deepdrft.db`
|
||||
- Entity: `TrackEntity` with fields for MediaPath, TrackName, Artist, Album, Genre, etc.
|
||||
- Context: `DeepDrftContext` with SQLite provider
|
||||
```
|
||||
DeepDrftWeb.Client (WASM)
|
||||
├── HttpClient "DeepDrft.API" ──► DeepDrftWeb host ──► EF Core / SQLite (metadata)
|
||||
└── HttpClient "DeepDrft.Content" ──► DeepDrftContent ──► FileDatabase / disk (binary)
|
||||
```
|
||||
|
||||
2. **FileDatabase**: Custom file-based storage system for binary media content
|
||||
- Located: `../Database/Vaults` (configurable via `filedatabase.json`)
|
||||
- Manages **MediaVaults** with different types: Media, Image, Audio
|
||||
- Supports structured binary storage with metadata (duration, bitrate, aspect ratio)
|
||||
1. **SQL Database (SQLite)**: Metadata and track info via Entity Framework
|
||||
- Location: `../Database/deepdrft.db`
|
||||
- Entity: `TrackEntity` with `Id`, `EntryKey`, `TrackName`, `Artist`, `Album?`, `Genre?`, `ReleaseDate?`, `ImagePath?`
|
||||
- Context: `DeepDrftContext` in `DeepDrftWeb.Services`
|
||||
|
||||
### FileDatabase System Details
|
||||
2. **FileDatabase**: Custom file-based binary storage system
|
||||
- Location: `../Database/Vaults` (configurable via `filedatabase.json`)
|
||||
- Root contains typed **MediaVaults** (Media, Image, Audio)
|
||||
- Each vault has a JSON `index` file listing entries + per-entry metadata
|
||||
- Entries are user-supplied strings sanitized to `[a-zA-Z0-9-]` + file extension
|
||||
- Binary hierarchy: `FileBinary` → `MediaBinary` (+ Extension/MIME) → `AudioBinary` (+ Duration/Bitrate) | `ImageBinary` (+ AspectRatio)
|
||||
- **Error-handling philosophy**: public operations swallow exceptions and return `null`/`false` — callers must check return values, not catch.
|
||||
|
||||
The FileDatabase is the core innovation of this project:
|
||||
## Key Architectural Decisions
|
||||
|
||||
- **Vault-based Organization**: Content organized into typed vaults (MediaVaultType: Media, Image, Audio)
|
||||
- **Index System**: Uses JSON-based indexing with DirectoryIndex and VaultIndex structures
|
||||
- **Media Types**:
|
||||
- `AudioBinary`: Buffer + Duration + Bitrate + Extension
|
||||
- `ImageBinary`: Buffer + AspectRatio + Extension
|
||||
- `MediaBinary`: Base buffer + Extension + MIME type support
|
||||
- **Factory Pattern**: `MediaVaultFactory` creates appropriate vault types
|
||||
- **Async Operations**: All database operations are async with error swallowing
|
||||
### Service projects vs. host projects
|
||||
|
||||
The split between `DeepDrftWeb` / `DeepDrftWeb.Services` (and the same for Content) is deliberate: hosts own HTTP surface, config, DI wiring; `*.Services` are plain class libraries holding domain logic. This lets `DeepDrftCli` reuse both `TrackService` implementations without taking ASP.NET dependencies.
|
||||
|
||||
**New domain logic goes in `*.Services` unless genuinely host-specific** (controllers, middleware, render-mode config, theme prerender).
|
||||
|
||||
### TrackEntity is a join, not a content blob
|
||||
|
||||
`TrackEntity` holds *only* metadata. The link to binary content is `EntryKey` (string) — the entry id inside the `tracks` vault in FileDatabase. Dual-database add flow:
|
||||
|
||||
1. `DeepDrftContent.Services.TrackService.AddTrackFromWavAsync` processes WAV, generates entry GUID, stores audio in vault, returns unpersisted `TrackEntity`.
|
||||
2. `DeepDrftWeb.Services.TrackService.Create` saves to SQLite and returns the persisted entity with `Id`.
|
||||
|
||||
If step 1 succeeds and step 2 fails, audio is orphaned in the vault (no rollback today).
|
||||
|
||||
### Streaming-first audio playback
|
||||
|
||||
The player is not fetch-then-play:
|
||||
|
||||
1. Client calls `GET api/track/{id}` on DeepDrftContent and receives WAV bytes as a stream (`HttpCompletionOption.ResponseHeadersRead`).
|
||||
2. `StreamingAudioPlayerService` reads in adaptive 16–64 KB chunks, pushes each via `AudioInteropService.processStreamingChunk`.
|
||||
3. TypeScript `StreamDecoder` parses WAV header, decodes chunks to `AudioBuffer`s. `PlaybackScheduler` schedules them on a Web Audio graph.
|
||||
4. Playback starts as soon as a min buffer is queued; UI duration from parsed header (not waiting for full file).
|
||||
5. **Seek beyond buffer**: if seek target is past what's decoded, client issues `GET api/track/{id}?offset={byteOffset}`. Server's `WavOffsetService` block-aligns offset, synthesises a fresh 44-byte WAV header, streams `[new header][data from offset]`. Player tears down and re-initialises decoder for the new stream.
|
||||
|
||||
Keep this seam clean — it is the most architecturally load-bearing part of the playback path.
|
||||
|
||||
### Theming and dark mode
|
||||
|
||||
- MudBlazor is the UI framework. Light and dark palettes (bespoke "Charleston in the Day" / "Lowcountry Summer Nights") defined inline in `MainLayout.razor`.
|
||||
- Dark mode toggles via cookie (`darkMode`, 365 days). Client-side via JS interop.
|
||||
- During server prerender, `DarkModeService` (in `DeepDrftWeb`) reads the cookie and seeds `DarkModeSettings.IsDarkMode`, which carries into WASM render via `PersistentComponentState`. Avoids "wrong theme flash" on initial paint.
|
||||
- `DarkModeSettings` lives in `DeepDrftWeb.Client.Common` (consumed by both server prerender and client components).
|
||||
- Typography: Google Fonts (Bodoni Moda, Cormorant, DM Sans). Hand-rolled gas-lamp icon (lit/unlit) lives in `DDIcons.cs`.
|
||||
|
||||
### TypeScript interop, not raw JS
|
||||
|
||||
Audio interop authored in TypeScript under `DeepDrftWeb/Interop/audio/`, compiled to `wwwroot/js/audio/` via `Microsoft.TypeScript.MSBuild`. One module per responsibility (AudioContextManager, StreamDecoder, PlaybackScheduler, SpectrumAnalyzer, AudioPlayer), plus `index.ts` exposing `window.DeepDrftAudio`. `tsconfig.json` is **not** copied to output. In dev, raw `.ts` served from `/Interop/` for source-map debugging.
|
||||
|
||||
## Development Commands
|
||||
|
||||
@@ -52,11 +94,7 @@ dotnet build DeepDrftHome.sln
|
||||
dotnet test DeepDrftTests/
|
||||
|
||||
# Run specific test class
|
||||
dotnet test DeepDrftTests/ --filter "FileDatabaseTests"
|
||||
|
||||
# Build specific project
|
||||
dotnet build DeepDrftWeb/
|
||||
dotnet build DeepDrftContent/
|
||||
dotnet test DeepDrftTests/ --filter "ClassName=FileDatabaseTests"
|
||||
```
|
||||
|
||||
### Running Applications
|
||||
@@ -66,48 +104,54 @@ dotnet run --project DeepDrftWeb
|
||||
|
||||
# Run content API
|
||||
dotnet run --project DeepDrftContent
|
||||
|
||||
# Run CLI (classic mode)
|
||||
dotnet run --project DeepDrftCli -- list
|
||||
|
||||
# Run CLI (GUI mode)
|
||||
dotnet run --project DeepDrftCli -- gui
|
||||
```
|
||||
|
||||
### Entity Framework (SQL Database)
|
||||
```bash
|
||||
# Add migration
|
||||
dotnet ef migrations add MigrationName --project DeepDrftWeb
|
||||
# Add migration (from solution root)
|
||||
dotnet ef migrations add MigrationName --project DeepDrftWeb.Services --startup-project DeepDrftWeb
|
||||
|
||||
# Update database
|
||||
dotnet ef database update --project DeepDrftWeb
|
||||
dotnet ef database update --project DeepDrftWeb.Services --startup-project DeepDrftWeb
|
||||
```
|
||||
|
||||
## Key Configuration Files
|
||||
|
||||
- `DeepDrftWeb/appsettings.json`: SQL connection string, logging configuration
|
||||
- `DeepDrftContent/environment/filedatabase.json`: FileDatabase vault path configuration
|
||||
- `DeepDrftContent/environment/apikey.json`: API authentication (not in repo)
|
||||
- `DeepDrftWeb/appsettings.json`: SQL connection string, logging
|
||||
- `DeepDrftContent/environment/filedatabase.json`: FileDatabase vault path
|
||||
- `DeepDrftContent/environment/apikey.json`: API key (not in repo)
|
||||
- `DeepDrftCli/environment/connections.json`: CLI config (`ConnectionString`, `VaultPath`)
|
||||
|
||||
## Folder-Level Guidance
|
||||
|
||||
Folder-level `CLAUDE.md` files provide specifics on structure, patterns, and commands for each project. Start with the project's folder `CLAUDE.md` when entering that directory. The root CLAUDE.md here sets the architectural context; folder files answer "what's in this folder and how do I work here?"
|
||||
|
||||
## Important Patterns
|
||||
|
||||
### FileDatabase Usage
|
||||
```csharp
|
||||
// Initialize FileDatabase
|
||||
var fileDatabase = await FileDatabase.FromAsync(rootPath);
|
||||
### Result Pattern (NetBlocks)
|
||||
|
||||
// Register audio content
|
||||
await fileDatabase.RegisterResourceAsync("tracks", trackId, audioBinary);
|
||||
Services return `Result`, `ResultContainer<T>`, or `ApiResult<T>` rather than throwing for expected failure modes. Catch at service boundary, surface via result.
|
||||
|
||||
// Load audio content
|
||||
var audio = await fileDatabase.LoadResourceAsync<AudioBinary>("tracks", trackId);
|
||||
```
|
||||
### Error Swallowing in FileDatabase
|
||||
|
||||
### Dependency Injection
|
||||
- FileDatabase service registration occurs in startup configuration
|
||||
- SQL context registered via `AddDbContext<DeepDrftContext>`
|
||||
- Repository pattern with `TrackRepository` and `TrackService`
|
||||
Public `Load*` / `Register*` operations in FileDatabase swallow exceptions and return `null` / `false`. Callers must check return values. This matches the TypeScript original and is load-bearing — do not change without a design pass.
|
||||
|
||||
### Testing Strategy
|
||||
- Comprehensive FileDatabase integration tests with temporary directories
|
||||
- Uses NUnit framework with async test patterns
|
||||
- Test isolation via unique temp directories per test
|
||||
- Covers vault creation, media storage, and retrieval scenarios
|
||||
### Pagination
|
||||
|
||||
## Project Dependencies
|
||||
Services build `PagingParameters<T>` with an `OrderBy` expression. Switch in the service maps a string sort column to the expression. New sort columns extend this switch. Nulls sort to end (padded sentinel strings / `DateOnly.MaxValue`).
|
||||
|
||||
External dependencies include Entity Framework Core (SQLite), MudBlazor, and the custom NetBlocks library. The FileDatabase system is entirely custom-built and forms the backbone of the media storage architecture.
|
||||
## External Dependencies
|
||||
|
||||
- Entity Framework Core 10.0.1 (SQLite)
|
||||
- MudBlazor 8.15.0
|
||||
- NUnit 4.4.0
|
||||
- NetBlocks (Result patterns)
|
||||
- Microsoft.TypeScript.MSBuild (TS compilation)
|
||||
|
||||
See individual project files for detailed dependency lists and versions.
|
||||
|
||||
Reference in New Issue
Block a user