# Music & VJ URL Structure

Unified URL schema for `music.html` and `vj.html`.

## URL Anatomy

```
https://example.com/vj.html?show=reggae&npub=npub1abc#/playlist/ep-1744382400
\______________________/\________/ \_______________/ \________________________/
        origin           pathname    query string         hash fragment
```

## Query String Parameters

Query string parameters represent **identity and session context**. They persist across hash navigation changes.

| Parameter | Format | Pages | Required | Description |
|-----------|--------|-------|----------|-------------|
| `npub` | `npub1...` | both | no | Target user public key in npub format (preferred) |
| `pubkey` | 64-char hex | both | no | Target user public key in hex format (fallback) |
| `auth` | `required` \| `optional` \| `none` | both | no | Authentication mode override |
| `show` | slug string | both | no | Active show slug (e.g. `saturday-reggae`) |
| `episode` | identifier string | both | no | Active episode playlist identifier |
| `a` | `30311:{pubkey}:{slug}` | vj.html | no | Stream coordinate for direct stream targeting |
| `naddr` | `naddr1...` | vj.html | no | Nostr address encoding of stream event |

### Parameter Precedence

- `npub` takes priority over `pubkey` when both are present
- `pubkey` is deleted from URL when `npub` is set
- If `npub` or `pubkey` points to a different user than the logged-in user, the page enters **read-only mode**

## Hash Fragment Routes

Hash routes represent **in-page navigation state**. Changing the hash does not reload the page.

| Route | Description |
|-------|-------------|
| `#/` | Home — empty search view |
| `#/search/{query}` | Search results for the given query |
| `#/album/{albumId}` | Album detail drill-down |
| `#/artist/{artistId}` | Artist detail drill-down |
| `#/track/{trackId}` | Play a specific track by ID |
| `#/playlist/{identifier}` | Open own playlist/episode by identifier |
| `#/playlist/{pubkey}/{identifier}` | Open external playlist/episode by pubkey and identifier |

### Route Parsing

Routes are parsed by splitting the hash on `/`:

```
#/playlist/abc123/ep-42  →  { page: 'playlist', parts: ['abc123', 'ep-42'] }
#/search/bob marley      →  { page: 'search', parts: ['bob marley'] }
#/track/36787:pubkey:dTag →  { page: 'track', parts: ['36787:pubkey:dTag'] }
```

## URL Examples

### VJ working on own show
```
vj.html?show=saturday-reggae&episode=ep-1744382400
```

### Sharing an episode for playback on music.html
```
music.html?npub=npub1abc123...#/playlist/npub1abc123.../ep-1744382400
```

### Sharing a VJ episode for viewing
```
vj.html?npub=npub1abc123...&show=saturday-reggae&episode=ep-1744382400
```

### Direct track link (works on either page)
```
music.html#/track/36787:aaaaaaaa...:my-track-dtag
vj.html#/track/36787:aaaaaaaa...:my-track-dtag
```

### Search link
```
music.html#/search/bob%20marley
```

### Album link
```
music.html#/album/album-id-here
```

## URL Update Behavior

| User Action | URL Change | Method |
|-------------|-----------|--------|
| Select show in dropdown | `?show={slug}` added/updated | `replaceState` |
| Select/create episode | `?episode={id}` added/updated | `replaceState` |
| Search for music | `#/search/{query}` | hash assignment |
| Click album/artist | `#/album/{id}` or `#/artist/{id}` | hash assignment |
| Play track from link | `#/track/{id}` (`id` is now typically `36787:pubkey:dTag`) | hash assignment |
| Select playlist | `#/playlist/{id}` | hash assignment |
| Login / auth change | `?npub={npub}` added | `replaceState` |
| Load external user | `?npub={npub}` or `?pubkey={hex}` | page navigation |

### replaceState vs hash assignment

- **`replaceState`** — Used for context changes (show, episode, auth). Does not create a new history entry. The user does not get a "back" step for every dropdown change.
- **Hash assignment** — Used for navigation changes (search, album, track, playlist). Creates a new history entry. Back/Forward buttons navigate between views.
