# Sprint G004 — Media Engine Foundation · Walkthrough

## What Was Built

The **Media Engine Foundation** is now live and integrated naturally into the existing Invitation Builder. No existing modules were modified beyond the minimum necessary.

---

## Files Created

| File | Purpose |
|------|---------|
| [MediaService.php](file:///c:/xampp/htdocs/inveetaire/app/services/MediaService.php) | Central upload/delete/serve engine. Validates MIME via `finfo`, UUID filenames, path traversal prevention. |
| [MediaController.php](file:///c:/xampp/htdocs/inveetaire/app/modules/Media/MediaController.php) | Thin HTTP handler — renders media page, handles AJAX upload/delete/serve. |
| [Media/routes.php](file:///c:/xampp/htdocs/inveetaire/app/modules/Media/routes.php) | Route definitions for all 4 media endpoints. |
| [media.php (view)](file:///c:/xampp/htdocs/inveetaire/app/views/invitation/media.php) | Full Media Manager UI — drag-drop, previews, live AJAX. |
| [G004_media_engine.sql](file:///c:/xampp/htdocs/inveetaire/database/migrations/G004_media_engine.sql) | Safe idempotent migration. |
| `storage/uploads/couples/.htaccess` | Denies direct web access to all couple media. |

## Files Modified

| File | Change |
|------|--------|
| [config/storage.php](file:///c:/xampp/htdocs/inveetaire/config/storage.php) | Added `media_base` path + `media_slots` config for all 7 slots. |
| [InvitationModel.php](file:///c:/xampp/htdocs/inveetaire/app/modules/Invitation/InvitationModel.php) | Added `getMediaSection()`, `saveMediaSection()`, `defaultMediaMeta()`. |
| [Invitation/routes.php](file:///c:/xampp/htdocs/inveetaire/app/modules/Invitation/routes.php) | Added `require` for Media module routes. |
| [couple.php (layout)](file:///c:/xampp/htdocs/inveetaire/app/views/layouts/couple.php) | Added `• Media` link in Invitation submenu. |

---

## Architecture

```
Invitation Builder Sidebar
├── • General    → /app/couple/invitation/edit
├── • Media      → /app/couple/invitation/media   ← NEW
├── • Design     → /app/couple/invitation/design
└── • Message Template
```

### Data Flow
```
Upload → MediaController@upload
      → MediaService::upload() — validates MIME, size, writes UUID file
      → InvitationModel::saveMediaSection() — persists path in JSON
      → JSON response → DOM update (no page reload)
```

### Storage Hierarchy
```
storage/uploads/couples/
    {workspace.slug}/
        hero/         ← 1 image
        couple/       ← max 2 images
        gallery/      ← max 10 images
        story/        ← max 5 images
        music_cover/  ← 1 image
        music_file/   ← 1 MP3
        gift/         ← 1 image (QRIS)
```

### JSON Metadata (in `invitation_sections.section_config`)
```json
{
  "hero":        "slug/hero/uuid.jpg",
  "couple":      ["slug/couple/a.jpg", "slug/couple/b.jpg"],
  "gallery":     ["slug/gallery/1.jpg", ...],
  "story":       ["slug/story/1.jpg", ...],
  "music_cover": "slug/music_cover/uuid.jpg",
  "music_file":  "slug/music_file/uuid.mp3",
  "gift":        "slug/gift/uuid.jpg"
}
```

---

## ⚠️ Required Action — Run Migration

> [!IMPORTANT]
> Before using the Media Manager, run the database migration with XAMPP MySQL started:
>
> **Option A — phpMyAdmin**: Import `database/migrations/G004_media_engine.sql`
>
> **Option B — CLI** (with XAMPP running):
> ```
> C:\xampp\mysql\bin\mysql.exe -u root -h 127.0.0.1 --database=inveetaire < database/migrations/G004_media_engine.sql
> ```
>
> This adds the unique constraint needed for the upsert and seeds empty media rows for all existing invitations.

---

## Future Sprint Extension Points

| Feature | Where to extend |
|---------|----------------|
| Image Cropper | `MediaService::upload()` → pipe through CropperService before write |
| WebP Conversion | `MediaService::upload()` → pipe through ImageOptimiserService |
| Music Player | Read `media['music_file']` + build serve URL in invitation theme |
| QR Generator | New slot `qr_code` in `config/storage.php` media_slots |
| Live Guest Media | New `guest_photos` slot — same MediaService, different controller |

---

## Validation Results

- ✅ `MediaService.php` — No syntax errors
- ✅ `MediaController.php` — No syntax errors
- ✅ `Media/routes.php` — No syntax errors
- ✅ `InvitationModel.php` — No syntax errors
- ✅ `Invitation/routes.php` — No syntax errors
- ✅ `media.php` (view) — No syntax errors
- ✅ `config/storage.php` — No syntax errors
