# Mukke - Music Workspace

Mukke is a web application for managing your music library, playing tracks, and creating synchronized lyrics. It combines a music player with a beat/lyrics editor featuring waveform visualization, BPM detection, timestamp markers, and exports to multiple formats.

## Architecture

```
apps/mukke/
├── apps/
│   ├── backend/          # NestJS API (port 3010)
│   ├── web/              # SvelteKit app (port 5180)
│   └── landing/          # Astro marketing page
├── packages/
│   └── shared/           # Shared types (@mukke/shared)
└── package.json
```

## Quick Start

```bash
# Start with full database setup
pnpm dev:mukke:full

# Or start components individually
pnpm docker:up                        # Start PostgreSQL, Redis, MinIO
pnpm --filter @mukke/backend dev      # Backend on port 3010
pnpm --filter @mukke/web dev          # Web on port 5180
pnpm --filter @mukke/landing dev      # Landing page
```

## Backend API Endpoints

### Songs (Library)
- `POST /songs/upload` - Upload song and get presigned URL
- `GET /songs` - List user's songs (with sort/filter)
- `GET /songs/:id` - Get song details
- `PUT /songs/:id` - Update song metadata
- `PUT /songs/:id/favorite` - Toggle favorite
- `PUT /songs/:id/play` - Increment play count
- `DELETE /songs/:id` - Delete song
- `GET /songs/search?q=` - Search songs
- `POST /songs/:id/extract-metadata` - Extract ID3 tags from file into DB (+ cover art to S3)
- `POST /songs/:id/write-tags` - Write DB metadata as ID3 tags back into MP3 file
- `GET /songs/:id/cover-url` - Get presigned URL for cover art

### Playlists
- `GET /playlists` - List user's playlists
- `POST /playlists` - Create playlist
- `GET /playlists/:id` - Get playlist with songs
- `PUT /playlists/:id` - Update playlist
- `DELETE /playlists/:id` - Delete playlist
- `POST /playlists/:id/songs` - Add song to playlist
- `DELETE /playlists/:id/songs/:songId` - Remove song
- `PUT /playlists/:id/songs/reorder` - Reorder songs

### Library (Aggregates)
- `GET /library/albums` - Get albums (grouped)
- `GET /library/artists` - Get artists (grouped)
- `GET /library/genres` - Get genres (grouped)
- `GET /library/stats` - Library statistics

### Projects (Editor)
- `GET /projects` - List user's projects
- `GET /projects/:id` - Get project with beat and lyrics
- `POST /projects` - Create project
- `POST /projects/from-song/:songId` - Create project from library song
- `PUT /projects/:id` - Update project
- `DELETE /projects/:id` - Delete project

### Beats
- `GET /beats/project/:projectId` - Get beat for project
- `GET /beats/:id` - Get beat with markers
- `GET /beats/:id/download-url` - Get presigned download URL
- `POST /beats/upload` - Create beat and get upload URL
- `PUT /beats/:id/metadata` - Update BPM, duration, waveform data
- `DELETE /beats/:id` - Delete beat

### Markers
- `GET /markers/beat/:beatId` - Get markers for beat
- `POST /markers` - Create marker
- `POST /markers/bulk` - Bulk create markers
- `PUT /markers/:id` - Update marker
- `PUT /markers/bulk` - Bulk update markers
- `DELETE /markers/:id` - Delete marker

### Lyrics
- `GET /lyrics/project/:projectId` - Get lyrics with synced lines
- `POST /lyrics/project/:projectId` - Create/update lyrics content
- `POST /lyrics/:id/sync` - Sync line timestamps

### Export
- `GET /export/:projectId?format=lrc|srt|json` - Export project

## Database Schema

```typescript
// songs - Music library
{ id, userId, title, artist, album, albumArtist, genre, trackNumber, year, duration,
  storagePath, coverArtPath, fileSize, bpm, favorite, playCount, lastPlayedAt, addedAt, updatedAt }

// playlists - User playlists
{ id, userId, name, description, coverArtPath, createdAt, updatedAt }

// playlist_songs - Playlist-Song join table
{ id, playlistId, songId, sortOrder, addedAt }

// projects - Editor projects
{ id, userId, title, description, songId, createdAt, updatedAt }

// beats - Audio files for editor
{ id, projectId, storagePath, filename, duration, bpm, bpmConfidence, waveformData }

// markers - Section markers
{ id, beatId, type, label, startTime, endTime, color, sortOrder }

// lyrics - Full lyrics text
{ id, projectId, content }

// lyric_lines - Synced lines
{ id, lyricsId, lineNumber, text, startTime, endTime }
```

## Supported Audio Formats

Playback uses HTML5 Audio (browser-native codec support). Upload accepts any `audio/*` MIME type.

| Format | Extensions | Browser Playback | Notes |
|--------|-----------|-----------------|-------|
| MP3 | `.mp3` | All browsers | ID3 tag read/write supported |
| WAV | `.wav` | All browsers | Uncompressed PCM |
| OGG Vorbis | `.ogg` | Chrome, Firefox, Edge | No Safari support |
| FLAC | `.flac` | All modern browsers | Lossless |
| AAC/M4A | `.aac`, `.m4a` | All browsers | Common iOS format |
| OPUS | `.opus` | Chrome, Firefox, Edge | Best quality/size ratio |
| WebM | `.webm` | Chrome, Firefox, Edge | Container format |
| AIFF | `.aiff`, `.aif` | Safari, Chrome | Common macOS format |
| WMA | `.wma` | Edge only | Legacy Windows format |
| ALAC | `.alac` | Safari | Apple Lossless |
| APE | `.ape` | None natively | Monkey's Audio (upload/metadata only) |
| WavPack | `.wv` | None natively | Hybrid lossless (upload/metadata only) |
| DSF/DFF | `.dsf`, `.dff` | None natively | DSD audio (upload/metadata only) |

**Note:** Formats without native browser playback can be uploaded and have metadata extracted (via `music-metadata`), but require server-side transcoding for playback (not yet implemented).

## Key Technologies

| Component | Technology |
|-----------|------------|
| Frontend | SvelteKit 2, Svelte 5, Tailwind CSS 4 |
| Waveform | wavesurfer.js 7.x |
| BPM Detection | Web Audio API (peak detection) |
| Metadata | music-metadata (server-side) |
| Backend | NestJS 10, Drizzle ORM |
| Database | PostgreSQL |
| Storage | MinIO (S3-compatible) |
| Auth | mana-core-auth |

## Environment Variables

### Backend (.env)
```
DATABASE_URL=postgresql://manacore:devpassword@localhost:5432/mukke
MANA_CORE_AUTH_URL=http://localhost:3001
S3_ENDPOINT=http://localhost:9000
S3_REGION=us-east-1
S3_ACCESS_KEY=minioadmin
S3_SECRET_KEY=minioadmin
S3_BUCKET=mukke-storage
```

### Web (.env)
```
PUBLIC_MANA_CORE_AUTH_URL=http://localhost:3001
PUBLIC_BACKEND_URL=http://localhost:3010
```

## Development Commands

```bash
# Database
pnpm --filter @mukke/backend db:push     # Push schema
pnpm --filter @mukke/backend db:studio   # Open Drizzle Studio

# Type checking
pnpm --filter @mukke/backend type-check
pnpm --filter @mukke/web type-check

# Build
pnpm --filter @mukke/backend build
pnpm --filter @mukke/web build
```
