# Droppy - Development Notes

## Overview

Droppy is a self-hosted web-based content casting system - like AirPlay/Chromecast but browser-based. Drop files or paste URLs to display content fullscreen on a connected display.

## Architecture

```
┌─────────────────┐      ┌─────────────────┐      ┌─────────────────┐
│  Upload Page    │ ───► │  Local Server   │ ───► │  Display Page   │
│  (browser tab)  │      │  (localhost)    │      │  (fullscreen)   │
└─────────────────┘      └─────────────────┘      └─────────────────┘
```

**Stack:**
- **Frontend:** SvelteKit with adapter-static
- **Backend:** Node.js + Express
- **Real-time:** Socket.IO for content sync
- **Media extraction:** yt-dlp + Cobalt API
- **Deployment:** Docker Compose

## Running the App

```bash
docker compose up -d --build
```

Access at `http://localhost:3001`

## Key Components

### Upload Page (`/src/routes/+page.svelte`)
- Drag-drop file upload
- URL paste for YouTube, Spotify, SoundCloud
- Recent items grid (click to recast)
- Status indicator showing display connection

### DropZone Component (`/src/lib/components/DropZone.svelte`)
- File upload via drag-drop or click
- URL input with platform detection
- Loading states with spinner
- Error toast notifications

### ContentDisplay (`/src/lib/components/ContentDisplay.svelte`)
- TV-friendly fullscreen display
- Cinematic blur backdrop from album art
- Large text readable from couch distance (uses `clamp()`)
- Platform-specific branding (Spotify green, SoundCloud orange)
- Embedded players for audio/video

## Design System

**Fonts:**
- Instrument Sans - UI text
- JetBrains Mono - monospace elements (room IDs, URLs)

**Colors:**
- Background: `#0C0C0C` (near black)
- Surface: `#161616`
- Border: `#2A2A2A`
- Text: `#FAFAFA`
- Accent: `#FF6B35` (coral)
- Success: `#4ADE80` (green)

## Docker Configuration Notes

### Build Fixes Applied

1. **No package-lock.json** - Changed Dockerfile to use `npm install` instead of `npm ci`

2. **Vite version conflict** - `@sveltejs/vite-plugin-svelte` requires vite@^5, so downgraded from ^6.0.0 to ^5.4.0

3. **youtube-dl-exec Python check** - Added `ENV YOUTUBE_DL_SKIP_PYTHON_CHECK=1` to skip Python requirement during install

4. **Missing favicon prerender error** - Added `prerender: { handleHttpError: 'warn' }` to svelte.config.js

5. **Port conflict** - Mapped container port 3000 to host port 3001 in docker-compose.yml

## File Structure

```
droppy/
├── src/
│   ├── lib/
│   │   ├── components/
│   │   │   ├── DropZone.svelte      # Upload interface
│   │   │   ├── ContentDisplay.svelte # Fullscreen display
│   │   │   └── MediaPlayer.svelte   # Video/audio player
│   │   ├── stores/
│   │   │   └── content.ts           # State management
│   │   └── socket.ts                # Socket.IO client
│   └── routes/
│       ├── +page.svelte             # Upload page
│       └── display/+page.svelte     # Display page
├── server/
│   ├── index.ts                     # Express + Socket.IO
│   └── services/
│       ├── contentExtractor.ts      # URL routing + playlist detection
│       ├── youtubeExtractor.ts      # YouTube extraction + playlists
│       ├── soundcloudExtractor.ts   # SoundCloud extraction + playlists
│       ├── spotifyExtractor.ts      # Spotify metadata extraction
│       ├── cobaltClient.ts          # Cobalt API (social media)
│       └── socketManager.ts         # Socket.IO event handlers + queue
├── Dockerfile
├── docker-compose.yml
├── docs/
│   └── SOUNDCLOUD_PLAYLISTS.md      # Playlist implementation details
└── examples/                        # Static HTML prototypes
```

## URL Handling

| URL Type | Handler |
|----------|---------|
| Direct media (.mp4, .jpg) | Display directly |
| YouTube | Extract via yt-dlp (supports playlists) |
| YouTube Playlist | Extract all videos, on-demand download |
| SoundCloud | Extract via yt-dlp |
| SoundCloud Playlist | Extract metadata, on-demand audio download |
| Spotify | Album art + search audio elsewhere |
| Twitter/TikTok/Instagram | Cobalt API |
| Other | oEmbed or iframe fallback |

## Playlist Support

Both YouTube and SoundCloud playlists are supported. See [docs/SOUNDCLOUD_PLAYLISTS.md](docs/SOUNDCLOUD_PLAYLISTS.md) for detailed implementation notes.

**Key features:**
- Paste playlist URL → all tracks queued with metadata
- On-demand audio extraction (first play downloads, subsequent plays use cache)
- Playlist grouping in UI (shows "Playlist Name • 3/15")
- Remove entire playlist with one click
- Rate-limited metadata fetching to avoid API blocks

**Cache locations:**
- YouTube: `/uploads/youtube-cache/`
- SoundCloud: `/uploads/soundcloud-cache/`
